squared 0.5.16 → 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.
@@ -10,9 +10,9 @@ module Squared
10
10
  include Common::Shell
11
11
  extend Forwardable
12
12
 
13
- OPT_NAME = /\A(?:(--)|-)((?(1)[^=\s-][^=\s]*|[^=\s-]))\z/
14
- OPT_VALUE = /\A-{0,2}([^=\s-][^=\s]*)(?:=|\s+)(\S.*)\z/
15
- OPT_SINGLE = /\A-([^=\s-])(.+)\z/
13
+ OPT_NAME = /\A(?:(--)|-)((?(1)[A-Za-z\d]+|[A-Za-z\d]))\z/
14
+ OPT_VALUE = /\A-{0,2}([^= ]+)(?: *= *| +)(.+)\z/
15
+ OPT_SINGLE = /\A-([A-Za-z\d])(.+)\z/
16
16
  private_constant :OPT_NAME, :OPT_VALUE, :OPT_SINGLE
17
17
 
18
18
  class << self
@@ -21,19 +21,33 @@ module Squared
21
21
  include Prompt
22
22
 
23
23
  def append(target, *args, delim: false, escape: false, quote: true, strip: nil, force: true, double: false,
24
- **)
24
+ filter: nil, **)
25
25
  return if (ret = args.flatten).empty?
26
26
 
27
27
  target << '--' if delim && !target.include?('--')
28
28
  if strip
29
29
  pat, s = Array(strip)
30
- ret.map! { |val| val.is_a?(String) ? val.gsub(pat, s || '') : val }
30
+ ret.map! { |val| val.gsub(pat, s || '') }
31
+ end
32
+ ret, err = ret.partition { |val| filter.match?(val) } if filter
33
+ if block_given?
34
+ out = []
35
+ err ||= []
36
+ ret.each do |val|
37
+ case (s = yield val)
38
+ when String
39
+ out << s
40
+ when NilClass, FalseClass
41
+ err << val
42
+ else
43
+ out << val
44
+ end
45
+ end
46
+ ret = out
31
47
  end
32
48
  if escape || quote
33
49
  ret.map! do |val|
34
- if opt?(val)
35
- val
36
- elsif escape
50
+ if escape
37
51
  shell_escape(val, quote: quote, double: double)
38
52
  else
39
53
  shell_quote(val, force: force, double: double)
@@ -45,13 +59,13 @@ module Squared
45
59
  else
46
60
  target.concat(ret)
47
61
  end
48
- ret
62
+ err || ret
49
63
  end
50
64
 
51
65
  def clear(target, opts, pass: true, styles: nil, **kwargs)
52
66
  return if opts.empty?
53
67
 
54
- kwargs[:subject] ||= stripext target.first
68
+ kwargs[:subject] ||= target.first.stripext
55
69
  kwargs[:hint] ||= 'unrecognized'
56
70
  append(target, opts, delim: true) if kwargs.delete(:append)
57
71
  warn log_message(Logger::WARN, opts.join(', '), pass: true, **kwargs)
@@ -86,24 +100,29 @@ module Squared
86
100
  ret.select { |val| single ? val.size == 1 : val.size > 1 }
87
101
  end
88
102
 
89
- def arg?(target, *args, value: false, **)
90
- r, s = args.partition { |val| val.is_a?(Regexp) }
91
- r << matchopts(s, value) unless s.empty?
92
- a = target.to_a
93
- if (n = a.index('--'))
94
- a = a[0..n]
103
+ def uniq!(list, pass = [])
104
+ keys = {}
105
+ list.each_with_index do |val, i|
106
+ j = val =~ OPT_VALUE ? $1 : val
107
+ (keys[j] ||= []) << i unless pass.include?(j)
95
108
  end
96
- r.any? { |pat| a.any?(pat) }
97
- end
109
+ data = keys.map { |item| item[1].size > 1 ? item[1][0..-2] : [] }.reject(&:empty?)
110
+ return if data.empty?
98
111
 
99
- def opt?(val)
100
- return false unless val.is_a?(String)
112
+ data.each { |key| key.each { |i| list[i] = nil } }
113
+ list.compact!
114
+ list
115
+ end
101
116
 
102
- val.start_with?('-') && (OPT_NAME.match?(val) || OPT_VALUE.match?(val) || OPT_SINGLE.match?(val))
117
+ def arg?(target, *args, value: false, **)
118
+ r, s = args.partition { |val| val.is_a?(Regexp) }
119
+ r << matchopts(s, value) unless s.empty?
120
+ s = target.to_a.compact
121
+ r.any? { |pat| s.any?(pat) }
103
122
  end
104
123
 
105
124
  def pattern?(val)
106
- val.match?(/(?:\A\^|\$\z)/) || val.match?(/(?:\.[*+]|\(\?:|\\[dsw]|\[.+\]|\{\d+,?\d*})/)
125
+ val.match?(/(?:\A\^|\$\z)/) || val.match?(/(?:\.[*+]|\(\?:|\\[dsw]|\[.+\]|\{\d+,?\d*\})/)
107
126
  end
108
127
 
109
128
  private
@@ -121,23 +140,22 @@ module Squared
121
140
  end
122
141
 
123
142
  def shortopt(*group)
124
- group.map! { |s| Regexp.escape(s.delete_prefix('-')) }
125
- "-(?:#{group.join('|')})(?:\\z|[^ =]| +[^ -])"
143
+ group.map! { |s| s.delete_prefix('-') }
144
+ "-(?:#{Regexp.escape(group.join('|'))})(?:\\z|[^ =]| +[^ -])"
126
145
  end
127
146
 
128
147
  def longopt(*group, value)
129
- group.map! { |s| Regexp.escape(s.delete_prefix('--')) }
130
- "--(?:#{group.join('|')})(?:#{value ? '=[^ ]| +[^ -]' : '[= ]|\z'})"
148
+ group.map! { |s| s.delete_prefix('--') }
149
+ "--(?:#{Regexp.escape(group.join('|'))})(?:#{value ? '=[^ ]| +[^ -]' : '[= ]|\z'})"
131
150
  end
132
151
  end
133
152
 
134
- attr_reader :target, :extras, :found, :errors, :values, :project, :path
153
+ attr_reader :target, :extras, :found, :errors, :values, :project, :path, :sep
135
154
 
136
155
  def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
137
- :merge, :compact, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
138
- def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :last, :shift, :unshift,
139
- :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select, :select!, :reject, :size,
140
- :delete_at
156
+ :merge, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
157
+ def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :shift, :unshift,
158
+ :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select, :select!, :reject, :size
141
159
 
142
160
  def_delegator :@extras, :delete, :remove
143
161
  def_delegator :@extras, :delete_at, :remove_at
@@ -145,10 +163,11 @@ module Squared
145
163
  def_delegator :@extras, :find_all, :detect_all
146
164
  def_delegator :@extras, :find_index, :detect_index
147
165
 
148
- def initialize(opts, list, target = Set.new, project: nil, path: nil, **kwargs, &blk)
166
+ def initialize(opts, list, target = Set.new, project: nil, path: nil, sep: '=', **kwargs, &blk)
149
167
  @target = target.is_a?(Set) ? target : target.to_set
150
168
  @project = project
151
169
  @path = path || project&.path
170
+ @sep = sep
152
171
  @errors = []
153
172
  @found = []
154
173
  parse(list, opts, **kwargs, &blk)
@@ -182,7 +201,7 @@ module Squared
182
201
  .each do |val|
183
202
  if (n = val.index('='))
184
203
  flag = val[0, n]
185
- case val[n + 1]
204
+ case val[n.succ]
186
205
  when 'e'
187
206
  e << flag
188
207
  when 'b'
@@ -207,7 +226,7 @@ module Squared
207
226
  else
208
227
  next
209
228
  end
210
- m << flag if flag.size == 1 && val[n + 2] == 'm'
229
+ m << flag if val[n + 2] == 'm'
211
230
  bare << flag if val.end_with?('?')
212
231
  else
213
232
  bare << val
@@ -216,7 +235,7 @@ module Squared
216
235
  no = (no || []).map { |val| (n = val.index('=')) ? val[0, n] : val }
217
236
  bare.concat(no)
218
237
  if underscore
219
- tr = ->(list) { list.map { |val| val.tr('-', '_') } }
238
+ tr = ->(a) { a.map { |val| val.tr('-', '_') } }
220
239
  @values.concat(tr.call(@values))
221
240
  bare.concat(tr.call(bare))
222
241
  e.concat(tr.call(e))
@@ -236,7 +255,7 @@ module Squared
236
255
  [f, /\A\d*(?:\.\d+)?\z/],
237
256
  [si, /\A-?\d+\z/]
238
257
  ].freeze
239
- numcheck = ->(k, v) { numtype.any? { |flag, pat| flag.include?(k) && pat.match?(v) } }
258
+ numcheck = ->(k, v) { numtype.any? { |flag, pat| flag.include?(k) && v.match?(pat) } }
240
259
  skip = false
241
260
  opts.each do |opt|
242
261
  next skip = true if opt == '--'
@@ -254,21 +273,21 @@ module Squared
254
273
  val = $2
255
274
  merge = m.include?(key)
256
275
  if e.include?(key)
257
- add shell_option(key, val, merge: merge)
276
+ add shell_option(key, val, merge: merge, sep: sep)
258
277
  elsif q.include?(key)
259
- add quote_option(key, val, double: qq.include?(key), merge: merge)
278
+ add quote_option(key, val, double: qq.include?(key), merge: merge, sep: sep)
260
279
  elsif p.include?(key)
261
280
  if val.match?(/\A(["']).+\1\z/)
262
- add shell_option(key, val, escape: false, merge: merge)
281
+ add shell_option(key, val, escape: false, merge: merge, sep: sep)
263
282
  elsif path
264
- add quote_option(key, path + val, merge: merge)
283
+ add quote_option(key, path + val, merge: merge, sep: sep)
265
284
  else
266
285
  push opt
267
286
  end
268
287
  elsif b.include?(key) || (bl.include?(key) && %w[true false].include?(val)) || numcheck.call(key, val)
269
- add basic_option(key, val, merge: merge)
288
+ add basic_option(key, val, merge: merge, sep: sep)
270
289
  elsif merge
271
- add basic_option(key, val, merge: true)
290
+ add basic_option(key, val, merge: true, sep: sep)
272
291
  else
273
292
  push opt
274
293
  end
@@ -280,28 +299,33 @@ module Squared
280
299
  skip = true if first&.any? { |s| s.is_a?(Regexp) ? opt.match?(s) : !opt.include?(s) }
281
300
  end
282
301
  end
283
- @values = @values.empty? ? /\A\s+\z/ : /\A(#{@values.join('|')})=(.+)\z/m
302
+ @values = @values.empty? ? /\A\s+\z/ : /\A(#{@values.join('|')})#{sep}(.+)\z/m
284
303
  @extras.each_with_index(&blk) if block_given?
285
304
  self
286
305
  end
287
306
 
288
- def swap(opts = nil)
307
+ def swap(opts = nil, &blk)
289
308
  unless opts
290
309
  opts = found
291
310
  @found = []
292
311
  end
312
+ opts.sort!(&blk) if block_given?
293
313
  @extras = opts
294
314
  self
295
315
  end
296
316
 
297
- def append(*args, **kwargs)
317
+ def append(*args, **kwargs, &blk)
298
318
  args = extras if args.empty?
299
- OptionPartition.append(target, *args, **kwargs)
319
+ out = OptionPartition.append(target, *args, **kwargs, &blk)
320
+ errors.concat(out) if out && (block_given? || kwargs[:filter])
300
321
  self
301
322
  end
302
323
 
303
324
  def append_any(*args, quote: true, **kwargs)
304
325
  (args.empty? ? extras : args.flatten).each do |val|
326
+ val = yield val if block_given?
327
+ next unless val.is_a?(String)
328
+
305
329
  if exist?(val)
306
330
  add_path(val, **kwargs)
307
331
  elsif quote
@@ -319,7 +343,7 @@ module Squared
319
343
  end
320
344
 
321
345
  def values_of(*args, strict: true, first: false, last: false)
322
- eq, s = strict ? ['=', '[^ ]+'] : ['(?:=| +)', '[^-][^ ]*']
346
+ eq, s = strict ? [sep, '[^ ]+'] : ['(?:=| +)', '[^-][^ ]*']
323
347
  g = ["\"((?:[^\"]|(?<=\\\\)\"(?!$#{windows? ? '| ' : ''}))*)\""]
324
348
  g << "'((?:[^']|'\\\\'')*)'" unless windows?
325
349
  g << "(#{s})"
@@ -358,23 +382,19 @@ module Squared
358
382
  def clear(opts = nil, errors: false, **kwargs)
359
383
  styles = project.theme[:inline] if project
360
384
  if errors
361
- OptionPartition.clear(target, self.errors, styles: styles, **kwargs)
362
- self.errors.clear
385
+ OptionPartition.clear(target, @errors, styles: styles, **kwargs)
386
+ @errors.clear
363
387
  return self unless opts
364
388
  end
365
389
  opts ||= extras
366
- OptionPartition.clear(target, if found.empty?
367
- opts
368
- else
369
- opts.reject { |val| found.include?(val) }
370
- end, styles: styles, **kwargs)
390
+ OptionPartition.clear(target, opts - found, styles: styles, **kwargs)
371
391
  opts.clear
372
392
  self
373
393
  end
374
394
 
375
395
  def adjoin(*args, with: nil, start: false)
376
- i = -1
377
- temp = compact
396
+ index = -1
397
+ temp = to_a
378
398
  if with
379
399
  pat = case with
380
400
  when String, Symbol
@@ -384,53 +404,114 @@ module Squared
384
404
  else
385
405
  with
386
406
  end
387
- temp.each_with_index do |val, index|
407
+ temp.each_with_index do |val, i|
388
408
  if val.to_s.match?(pat)
389
- i = index + (start.is_a?(Numeric) ? start : 1)
409
+ index = i + (start.is_a?(Numeric) ? start : 1)
390
410
  break
391
411
  end
392
412
  end
393
413
  else
394
- temp.each_with_index do |val, index|
395
- if i == 0
396
- next unless val.is_a?(String) && val.start_with?('-')
414
+ temp.each_with_index do |val, i|
415
+ if index == 0
416
+ next unless val.start_with?('-')
397
417
 
398
- i = index
418
+ index = i
399
419
  break
400
- elsif index > 0 && !val.to_s.start_with?('-')
420
+ elsif i > 0 && !val.start_with?('-')
401
421
  if start
402
- i = index + (start.is_a?(Numeric) ? start : 1)
422
+ index = i + (start.is_a?(Numeric) ? start : 1)
403
423
  break
404
424
  end
405
- i = 0
425
+ index = 0
406
426
  end
407
427
  end
408
428
  end
409
- if i > 0
429
+ if index > 0
410
430
  if args.empty?
411
431
  args = dup
412
432
  reset
413
433
  else
414
434
  args.each { |val| remove val }
415
435
  end
416
- args = temp[0...i] + args + temp[i..-1]
436
+ args = temp[0...index] + args + temp[index..-1]
417
437
  target.clear
418
438
  end
419
439
  merge args
420
440
  self
421
441
  end
422
442
 
423
- def add_path(*args, **kwargs)
424
- add shell_quote(path ? path.join(*args) : File.join(*args), option: false, **kwargs)
443
+ def add_path(*args, force: true, double: false, **kwargs)
444
+ if args.empty?
445
+ args = select { |s| s.is_a?(String) }
446
+ args.map! { |val| path + val } if path
447
+ append(args, force: force, **kwargs)
448
+ else
449
+ add shell_quote(path ? path.join(*args) : File.join(*args), option: false, force: force, double: double)
450
+ end
425
451
  self
426
452
  end
427
453
 
428
454
  def add_quote(*args, **kwargs)
429
- args.compact!
430
- merge(args.map! { |val| val == '--' || OptionPartition.opt?(val) ? val : shell_quote(val, **kwargs) })
455
+ merge(args.compact.map! { |val| val == '--' ? val : shell_quote(val, **kwargs) })
456
+ self
457
+ end
458
+
459
+ def add_option(flag, val = nil, **kwargs)
460
+ add shell_option(flag, val, **kwargs)
431
461
  self
432
462
  end
433
463
 
464
+ def add_first(fallback = nil, prefix: nil, path: false, quote: false, reverse: false, expect: false, **kwargs)
465
+ val = (reverse ? pop : shift) || fallback
466
+ if val
467
+ val.delete_prefix!(prefix) if prefix
468
+ return self if block_given? && !(val = yield val).is_a?(String)
469
+
470
+ if path
471
+ add_path(val, **kwargs)
472
+ elsif quote
473
+ add_quote(val, **kwargs)
474
+ else
475
+ add val
476
+ end
477
+ elsif expect
478
+ raise(expect.is_a?(String) ? expect : 'no value provided')
479
+ end
480
+ self
481
+ end
482
+
483
+ def last(val = nil, &blk)
484
+ unless block_given?
485
+ case val
486
+ when NilClass
487
+ return extras.last
488
+ when Numeric
489
+ return extras.last(val)
490
+ when String, Array, Regexp
491
+ val = OptionPartition.send(:matchopts, val) unless val.is_a?(Regexp)
492
+ blk = proc { |s| s&.match?(val) }
493
+ else
494
+ raise TypeError, "unknown: #{val}"
495
+ end
496
+ end
497
+ ret = find_all(&blk)
498
+ unless ret.empty?
499
+ ret = case val
500
+ when NilClass
501
+ ret.first(1)
502
+ when Numeric
503
+ ret.first(val)
504
+ else
505
+ ret
506
+ end
507
+ ret.each do |opt|
508
+ delete opt
509
+ add opt
510
+ end
511
+ end
512
+ val.nil? ? ret.first : ret
513
+ end
514
+
434
515
  def splice(*exclude, quote: true, delim: true, path: false, pattern: false, &blk)
435
516
  found, other = if block_given?
436
517
  partition(&blk)
@@ -464,7 +545,7 @@ module Squared
464
545
  self
465
546
  end
466
547
 
467
- def append?(key, val = nil, type: nil, force: false, **kwargs)
548
+ def append?(key, val = nil, type: nil, force: false, sep: '=', **kwargs)
468
549
  return false unless force || !arg?(key)
469
550
 
470
551
  val = yield self if block_given?
@@ -473,11 +554,11 @@ module Squared
473
554
  type ||= :quote if kwargs.empty?
474
555
  add case type
475
556
  when :quote
476
- quote_option(key, val)
557
+ quote_option(key, val, sep: sep)
477
558
  when :basic
478
- basic_option(key, val)
559
+ basic_option(key, val, sep: sep)
479
560
  else
480
- shell_option(key, val, **kwargs)
561
+ shell_option(key, val, sep: sep, **kwargs)
481
562
  end
482
563
  true
483
564
  end
@@ -486,24 +567,23 @@ module Squared
486
567
  OptionPartition.arg?(target, *args, **kwargs)
487
568
  end
488
569
 
489
- def exist?(*args, add: false, first: false, last: false)
490
- return false unless path
491
- return path.join(*args).exist? unless args.empty?
570
+ def exist?(*args, add: false, first: false, last: false, glob: false)
571
+ return with_glob?(File.join(*args), glob) unless args.empty?
492
572
 
493
573
  if first || last
494
- return false unless (val = first ? self.first : self.last).is_a?(String)
574
+ return false unless (val = first ? self.first : self.last)
495
575
 
496
- path.join(val).exist?.tap do |ret|
576
+ with_glob?(val, glob).tap do |ret|
497
577
  next unless add && ret
498
578
 
499
- add_path(first ? shift : pop)
579
+ add_first(path: true, reverse: !first)
500
580
  end
501
581
  else
502
- each_with_index do |val, index|
503
- next unless val.is_a?(String) && path.join(val).exist?
582
+ each_with_index do |val, i|
583
+ next unless with_glob?(val, glob)
504
584
 
505
585
  if add
506
- remove_at index
586
+ remove_at i
507
587
  add_path val
508
588
  end
509
589
  return true
@@ -525,8 +605,13 @@ module Squared
525
605
  val[OPT_VALUE, 1] || val
526
606
  end
527
607
 
608
+ def with_glob?(val, glob = true)
609
+ return false unless path && val.is_a?(String) && !val.empty?
610
+
611
+ path.join(val).exist? || (glob && !path.glob(val).empty?)
612
+ end
613
+
528
614
  def windows?
529
- require 'rake'
530
615
  Rake::Win32.windows?
531
616
  end
532
617
  end
@@ -536,50 +621,40 @@ module Squared
536
621
  super[/[^:]+\z/, 0]
537
622
  end
538
623
 
539
- alias to_ary to_a
540
-
541
- attr_reader :delim, :extras
624
+ attr_reader :delim
542
625
 
543
- def initialize(data = [], delim: ' ', partition: '--', uniq: /\A--?[^=\s-][^=\s]*(?:=|\s+)\S/)
544
- @delim = delim
545
- @partition = partition
546
- @uniq = uniq
547
- @extras = []
626
+ def initialize(data = [], delim: ' ')
548
627
  super(data.compact)
549
- end
550
-
551
- def compact
552
- to_ary.map!(&:to_s).reject(&:empty?)
628
+ @delim = delim
553
629
  end
554
630
 
555
631
  def last(val, pat)
556
- (@last ||= []).append([val, pat, $1]) if val =~ pat
632
+ (@last ||= []).push([val, pat, $1]) if val =~ pat
557
633
  self << val
558
634
  end
559
635
 
560
636
  def pass(&blk)
561
- ret = compact
637
+ ret = to_a.map!(&:to_s).reject(&:empty?)
562
638
  @last&.each do |val, pat, key|
563
- i = []
564
- j = nil
565
- ret.each_with_index do |opt, index|
639
+ items = []
640
+ index = nil
641
+ ret.each_with_index do |opt, i|
566
642
  if opt == val
567
- j = index
568
- elsif j && opt[pat, 1] == key
569
- i << index
643
+ index = i
644
+ elsif index && opt[pat, 1] == key
645
+ items << i
570
646
  end
571
647
  end
572
- next unless j && !i.empty?
648
+ next unless index && !items.empty?
573
649
 
574
- val = ret[j]
575
- cur = j
576
- i.each do |k|
650
+ val = ret[index]
651
+ cur = index
652
+ items.each do |k|
577
653
  ret[cur] = ret[k]
578
654
  cur = k
579
655
  end
580
- ret[i.last] = val
656
+ ret[items.last] = val
581
657
  end
582
- ret.concat(extras.map(&:to_s).reject(&:empty?)) unless extras.empty?
583
658
  return ret unless block_given?
584
659
 
585
660
  ret.reject(&blk)
@@ -601,73 +676,18 @@ module Squared
601
676
 
602
677
  def temp(*args, &blk)
603
678
  args.compact!
604
- pass(&blk)
605
- .concat(args)
606
- .join(@delim)
679
+ ret = pass(&blk)
680
+ ret = Set.new(ret.concat(args)).to_a unless args.empty?
681
+ ret.join(@delim)
607
682
  end
608
683
 
609
684
  def done
610
685
  to_s.tap { clear }
611
686
  end
612
687
 
613
- def merge(enum)
614
- if !extras.empty?
615
- extras.concat(enum.to_a)
616
- self
617
- elsif (n = enum.find_index { |val| extras?(val) })
618
- data = enum.to_a
619
- @extras = if n == 0
620
- data
621
- else
622
- super(data[0...n])
623
- data[n..-1]
624
- end
625
- self
626
- else
627
- super
628
- end
629
- end
630
-
631
- def <<(obj)
632
- extras!(obj) || super
633
- end
634
-
635
- def add?(obj)
636
- extras!(obj) || super
637
- end
638
-
639
- def to_a
640
- pass
641
- end
642
-
643
688
  def to_s
644
689
  pass.join(@delim)
645
690
  end
646
-
647
- def to_enum(*args)
648
- pass.to_enum(*args)
649
- end
650
-
651
- def to_json(*args)
652
- pass.to_json(*args)
653
- end
654
-
655
- def to_yaml(*args)
656
- pass.to_yaml(*args)
657
- end
658
-
659
- private
660
-
661
- def extras!(obj)
662
- return if extras.empty? && !extras?(obj)
663
-
664
- extras << obj unless !extras.include?(@partition) && include?(obj) && @uniq.match?(obj.to_s)
665
- self
666
- end
667
-
668
- def extras?(obj)
669
- obj == @partition || (include?(obj) && !@uniq.match?(obj.to_s))
670
- end
671
691
  end
672
692
  end
673
693
  end