squared 0.6.2 → 0.6.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +23 -13
- data/lib/squared/common/base.rb +2 -2
- data/lib/squared/common/format.rb +39 -32
- data/lib/squared/common/prompt.rb +6 -4
- data/lib/squared/common/shell.rb +4 -4
- data/lib/squared/config.rb +6 -7
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +72 -23
- data/lib/squared/workspace/project/base.rb +145 -83
- data/lib/squared/workspace/project/docker.rb +15 -15
- data/lib/squared/workspace/project/git.rb +74 -27
- data/lib/squared/workspace/project/node.rb +211 -109
- data/lib/squared/workspace/project/python.rb +9 -9
- data/lib/squared/workspace/project/ruby.rb +29 -34
- data/lib/squared/workspace/project/support/class.rb +36 -652
- data/lib/squared/workspace/project/support/optionpartition.rb +641 -0
- data/lib/squared/workspace/project.rb +0 -1
- data/lib/squared/workspace/repo.rb +23 -14
- data/lib/squared/workspace/series.rb +10 -8
- data/squared.gemspec +2 -2
- metadata +3 -3
- data/lib/squared/workspace/project/support.rb +0 -3
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module Squared
|
|
6
|
+
module Workspace
|
|
7
|
+
module Project
|
|
8
|
+
module Support
|
|
9
|
+
class OptionPartition
|
|
10
|
+
include Common::Shell
|
|
11
|
+
extend Forwardable
|
|
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/
|
|
16
|
+
private_constant :OPT_NAME, :OPT_VALUE, :OPT_SINGLE
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
include Common::Format
|
|
20
|
+
include Shell
|
|
21
|
+
include Prompt
|
|
22
|
+
|
|
23
|
+
def append(target, *args, delim: false, escape: false, quote: true, strip: nil, force: true, double: false,
|
|
24
|
+
filter: nil, **)
|
|
25
|
+
return if (ret = args.flatten).empty?
|
|
26
|
+
|
|
27
|
+
target << '--' if delim && !target.include?('--')
|
|
28
|
+
if strip
|
|
29
|
+
pat, s = Array(strip)
|
|
30
|
+
ret.map! { |val| val.is_a?(String) ? val.gsub(pat, s || '') : val }
|
|
31
|
+
end
|
|
32
|
+
ret, err = ret.partition { |val| filter.match?(val.to_s) } 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
|
|
47
|
+
end
|
|
48
|
+
if escape || quote
|
|
49
|
+
ret.map! do |val|
|
|
50
|
+
if opt?(val)
|
|
51
|
+
val
|
|
52
|
+
elsif escape
|
|
53
|
+
shell_escape(val, quote: quote, double: double)
|
|
54
|
+
else
|
|
55
|
+
shell_quote(val, force: force, double: double)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
if target.is_a?(Set)
|
|
60
|
+
target.merge(ret)
|
|
61
|
+
else
|
|
62
|
+
target.concat(ret)
|
|
63
|
+
end
|
|
64
|
+
err || ret
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def clear(target, opts, pass: true, styles: nil, **kwargs)
|
|
68
|
+
return if opts.empty?
|
|
69
|
+
|
|
70
|
+
kwargs[:subject] ||= target.first.stripext
|
|
71
|
+
kwargs[:hint] ||= 'unrecognized'
|
|
72
|
+
append(target, opts, delim: true) if kwargs.delete(:append)
|
|
73
|
+
warn log_message(Logger::WARN, opts.join(', '), pass: true, **kwargs)
|
|
74
|
+
exit 1 unless pass || confirm("Run? [#{sub_style(target, styles)}]", 'N')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def delete_key(target, *args, value: false, reverse: false, count: -1)
|
|
78
|
+
ret = []
|
|
79
|
+
args.each do |val|
|
|
80
|
+
next if (opts = target.grep(matchopt(val, value))).empty?
|
|
81
|
+
|
|
82
|
+
opts = opts.first(count) if count >= 0
|
|
83
|
+
opts.send(reverse ? :reverse_each : :each) { |key| target.delete(key) }
|
|
84
|
+
ret.concat(opts)
|
|
85
|
+
end
|
|
86
|
+
ret
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def strip(val)
|
|
90
|
+
return [] unless val
|
|
91
|
+
|
|
92
|
+
val = shell_split val if val.is_a?(String)
|
|
93
|
+
val.map { |s| s.sub(OPT_SINGLE, '\1=\2').sub(OPT_VALUE, '\1=\2').sub(OPT_NAME, '\2') }.reject(&:empty?)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def select(list, bare: true, no: true, single: false, double: false)
|
|
97
|
+
ret = bare ? list.grep_v(/=/) : list.grep(/=/).map! { |val| val.split('=', 2).first }
|
|
98
|
+
ret.map! { |val| val.split('|', 2).last }
|
|
99
|
+
ret = ret.grep_v(/\Ano-/) unless no
|
|
100
|
+
return ret if single == double
|
|
101
|
+
|
|
102
|
+
ret.select { |val| single ? val.size == 1 : val.size > 1 }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def uniq!(list, pass = [])
|
|
106
|
+
keys = {}
|
|
107
|
+
list.each_with_index do |val, i|
|
|
108
|
+
j = val =~ OPT_VALUE ? $1 : val
|
|
109
|
+
(keys[j] ||= []) << i unless pass.include?(j)
|
|
110
|
+
end
|
|
111
|
+
data = keys.map { |item| item[1].size > 1 ? item[1][0..-2] : [] }.reject(&:empty?)
|
|
112
|
+
return if data.empty?
|
|
113
|
+
|
|
114
|
+
data.each { |key| key.each { |i| list[i] = nil } }
|
|
115
|
+
list.compact!
|
|
116
|
+
list
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def arg?(target, *args, value: false, **)
|
|
120
|
+
r, s = args.partition { |val| val.is_a?(Regexp) }
|
|
121
|
+
r << matchopts(s, value) unless s.empty?
|
|
122
|
+
a = target.to_a
|
|
123
|
+
if (n = a.index('--'))
|
|
124
|
+
a = a[0..n]
|
|
125
|
+
end
|
|
126
|
+
r.any? { |pat| a.any?(pat) }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def opt?(val)
|
|
130
|
+
return false unless val.is_a?(String)
|
|
131
|
+
|
|
132
|
+
val.start_with?('-') && (OPT_NAME.match?(val) || OPT_VALUE.match?(val) || OPT_SINGLE.match?(val))
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def pattern?(val)
|
|
136
|
+
val.match?(/(?:\A\^|\$\z)/) || val.match?(/(?:\.[*+]|\(\?:|\\[dsw]|\[.+\]|\{\d+,?\d*})/)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def matchopt(val, value = false)
|
|
142
|
+
/\A#{val.size == 1 ? shortopt(val) : longopt(val, value)}/
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def matchopts(list, value = false)
|
|
146
|
+
a, b = Array(list).partition { |val| val.size == 1 || val.match?(OPT_SINGLE) }
|
|
147
|
+
return /\A#{shortopt(*a)}}/ if b.empty?
|
|
148
|
+
return /\A#{longopt(*b, value)}/ if a.empty?
|
|
149
|
+
|
|
150
|
+
/\A(?:#{shortopt(*a)}|#{longopt(*b, value)})/
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def shortopt(*group)
|
|
154
|
+
group.map! { |s| Regexp.escape(s.delete_prefix('-')) }
|
|
155
|
+
"-(?:#{group.join('|')})(?:\\z|[^ =]| +[^ -])"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def longopt(*group, value)
|
|
159
|
+
group.map! { |s| Regexp.escape(s.delete_prefix('--')) }
|
|
160
|
+
"--(?:#{group.join('|')})(?:#{value ? '=[^ ]| +[^ -]' : '[= ]|\z'})"
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
attr_reader :target, :extras, :found, :errors, :values, :project, :path, :sep
|
|
165
|
+
|
|
166
|
+
def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
|
|
167
|
+
:merge, :compact, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
|
|
168
|
+
def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :shift, :unshift,
|
|
169
|
+
:pop, :push, :concat, :index, :join, :detect, :map, :map!, :select, :select!, :slice, :slice!,
|
|
170
|
+
:reject, :size
|
|
171
|
+
|
|
172
|
+
def_delegator :@extras, :delete, :remove
|
|
173
|
+
def_delegator :@extras, :delete_at, :remove_at
|
|
174
|
+
def_delegator :@extras, :delete_if, :remove_if
|
|
175
|
+
def_delegator :@extras, :find_all, :detect_all
|
|
176
|
+
def_delegator :@extras, :find_index, :detect_index
|
|
177
|
+
|
|
178
|
+
def initialize(opts, list, target = Set.new, project: nil, path: nil, sep: '=', **kwargs, &blk)
|
|
179
|
+
@target = target.is_a?(Set) ? target : target.to_set
|
|
180
|
+
@project = project
|
|
181
|
+
@path = path || project&.path
|
|
182
|
+
@sep = sep
|
|
183
|
+
@errors = []
|
|
184
|
+
@found = []
|
|
185
|
+
parse(list, opts, **kwargs, &blk)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def parse(list, opts = extras, no: nil, single: nil, args: false, first: nil, underscore: nil, &blk)
|
|
189
|
+
@extras = []
|
|
190
|
+
@values = []
|
|
191
|
+
bare = []
|
|
192
|
+
e = []
|
|
193
|
+
b = []
|
|
194
|
+
m = []
|
|
195
|
+
p = []
|
|
196
|
+
q = []
|
|
197
|
+
qq = []
|
|
198
|
+
i = []
|
|
199
|
+
f = []
|
|
200
|
+
si = []
|
|
201
|
+
bl = []
|
|
202
|
+
ml = []
|
|
203
|
+
list.flat_map do |val|
|
|
204
|
+
x, y = val.split('|', 2)
|
|
205
|
+
if y
|
|
206
|
+
if (n = val.index('='))
|
|
207
|
+
x += val[n..-1]
|
|
208
|
+
end
|
|
209
|
+
[x, y]
|
|
210
|
+
else
|
|
211
|
+
x
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
.each do |val|
|
|
215
|
+
if (n = val.index('='))
|
|
216
|
+
flag = val[0, n]
|
|
217
|
+
case val[n.succ]
|
|
218
|
+
when 'e'
|
|
219
|
+
e << flag
|
|
220
|
+
when 'b'
|
|
221
|
+
b << flag
|
|
222
|
+
when 'm'
|
|
223
|
+
m << flag
|
|
224
|
+
when 'q'
|
|
225
|
+
qq << flag if val[n + 2] == 'q'
|
|
226
|
+
q << flag
|
|
227
|
+
when 'p'
|
|
228
|
+
p << flag
|
|
229
|
+
when 'i'
|
|
230
|
+
i << flag
|
|
231
|
+
when 'f'
|
|
232
|
+
f << flag
|
|
233
|
+
when 'n'
|
|
234
|
+
si << flag
|
|
235
|
+
when 'v'
|
|
236
|
+
@values << Regexp.escape(flag)
|
|
237
|
+
when '!'
|
|
238
|
+
bl << flag
|
|
239
|
+
when '+'
|
|
240
|
+
ml << flag
|
|
241
|
+
bare << flag
|
|
242
|
+
else
|
|
243
|
+
next
|
|
244
|
+
end
|
|
245
|
+
m << flag if val[n + 2] == 'm'
|
|
246
|
+
bare << flag if val.end_with?('?')
|
|
247
|
+
else
|
|
248
|
+
bare << val
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
no = (no || []).map { |val| (n = val.index('=')) ? val[0, n] : val }
|
|
252
|
+
bare.concat(no)
|
|
253
|
+
if underscore
|
|
254
|
+
tr = ->(a) { a.map { |val| val.tr('-', '_') } }
|
|
255
|
+
@values.concat(tr.call(@values))
|
|
256
|
+
bare.concat(tr.call(bare))
|
|
257
|
+
e.concat(tr.call(e))
|
|
258
|
+
b.concat(tr.call(b))
|
|
259
|
+
m.concat(tr.call(m))
|
|
260
|
+
p.concat(tr.call(p))
|
|
261
|
+
q.concat(tr.call(q))
|
|
262
|
+
qq.concat(tr.call(qq))
|
|
263
|
+
i.concat(tr.call(i))
|
|
264
|
+
f.concat(tr.call(f))
|
|
265
|
+
si.concat(tr.call(si))
|
|
266
|
+
bl.concat(tr.call(bl))
|
|
267
|
+
ml.concat(tr.call(ml))
|
|
268
|
+
no.concat(tr.call(no))
|
|
269
|
+
end
|
|
270
|
+
target.multiple = ml.map { |val| val.size == 1 ? "-#{val}" : "--#{val}" } if target.is_a?(JoinSet)
|
|
271
|
+
numtype = [
|
|
272
|
+
[i, /\A\d+\z/],
|
|
273
|
+
[f, /\A\d*(?:\.\d+)?\z/],
|
|
274
|
+
[si, /\A-?\d+\z/]
|
|
275
|
+
].freeze
|
|
276
|
+
numcheck = ->(k, v) { numtype.any? { |flag, pat| flag.include?(k) && v.match?(pat) } }
|
|
277
|
+
skip = false
|
|
278
|
+
opts.each do |opt|
|
|
279
|
+
next skip = true if opt == '--'
|
|
280
|
+
next push opt if skip
|
|
281
|
+
|
|
282
|
+
if single&.match?(opt)
|
|
283
|
+
add "-#{opt}"
|
|
284
|
+
elsif bare.include?(opt)
|
|
285
|
+
add(opt.size == 1 ? "-#{opt}" : "--#{opt}")
|
|
286
|
+
elsif opt.start_with?(/no[-_]/) && no.include?(name = opt[3..-1])
|
|
287
|
+
add "--no-#{name}"
|
|
288
|
+
else
|
|
289
|
+
if opt =~ OPT_VALUE
|
|
290
|
+
key = $1
|
|
291
|
+
val = $2
|
|
292
|
+
merge = m.include?(key)
|
|
293
|
+
if e.include?(key)
|
|
294
|
+
add shell_option(key, val, merge: merge, sep: sep)
|
|
295
|
+
elsif q.include?(key)
|
|
296
|
+
add quote_option(key, val, double: qq.include?(key), merge: merge, sep: sep)
|
|
297
|
+
elsif p.include?(key)
|
|
298
|
+
if val.match?(/\A(["']).+\1\z/)
|
|
299
|
+
add shell_option(key, val, escape: false, merge: merge, sep: sep)
|
|
300
|
+
elsif path
|
|
301
|
+
add quote_option(key, path + val, merge: merge, sep: sep)
|
|
302
|
+
else
|
|
303
|
+
push opt
|
|
304
|
+
end
|
|
305
|
+
elsif b.include?(key) || (bl.include?(key) && %w[true false].include?(val)) || numcheck.call(key, val)
|
|
306
|
+
add basic_option(key, val, merge: merge, sep: sep)
|
|
307
|
+
elsif merge
|
|
308
|
+
add basic_option(key, val, merge: true, sep: sep)
|
|
309
|
+
else
|
|
310
|
+
push opt
|
|
311
|
+
end
|
|
312
|
+
opt = key
|
|
313
|
+
else
|
|
314
|
+
push opt
|
|
315
|
+
skip = true if args
|
|
316
|
+
end
|
|
317
|
+
skip = true if first&.any? { |s| s.is_a?(Regexp) ? opt.match?(s) : !opt.include?(s) }
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
@values = @values.empty? ? /\A\s+\z/ : /\A(#{@values.join('|')})#{sep}(.+)\z/m
|
|
321
|
+
@extras.each_with_index(&blk) if block_given?
|
|
322
|
+
self
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def swap(opts = nil, &blk)
|
|
326
|
+
unless opts
|
|
327
|
+
opts = found
|
|
328
|
+
@found = []
|
|
329
|
+
end
|
|
330
|
+
opts.sort!(&blk) if block_given?
|
|
331
|
+
@extras = opts
|
|
332
|
+
self
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def append(*args, **kwargs, &blk)
|
|
336
|
+
args = extras if args.empty?
|
|
337
|
+
out = OptionPartition.append(target, *args, **kwargs, &blk)
|
|
338
|
+
errors.concat(out) if out && (block_given? || kwargs[:filter])
|
|
339
|
+
self
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def append_any(*args, quote: true, **kwargs)
|
|
343
|
+
(args.empty? ? extras : args.flatten).each do |val|
|
|
344
|
+
val = yield val if block_given?
|
|
345
|
+
next unless val.is_a?(String)
|
|
346
|
+
|
|
347
|
+
if exist?(val)
|
|
348
|
+
add_path(val, **kwargs)
|
|
349
|
+
elsif quote
|
|
350
|
+
add_quote(val, **kwargs)
|
|
351
|
+
else
|
|
352
|
+
add val
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
self
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def delete_key(*args, **kwargs)
|
|
359
|
+
OptionPartition.delete_key(target, *args, **kwargs)
|
|
360
|
+
self
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def values_of(*args, strict: true, first: false, last: false)
|
|
364
|
+
eq, s = strict ? [sep, '[^ ]+'] : ['(?:=| +)', '[^-][^ ]*']
|
|
365
|
+
g = ["\"((?:[^\"]|(?<=\\\\)\"(?!$#{'| ' if windows?}))*)\""]
|
|
366
|
+
g << "'((?:[^']|'\\\\'')*)'" unless windows?
|
|
367
|
+
g << "(#{s})"
|
|
368
|
+
args.map! do |opt|
|
|
369
|
+
if opt.size == 1
|
|
370
|
+
/(?:\A| )-#{opt} ?([^ ]+)/
|
|
371
|
+
else
|
|
372
|
+
/(?:\A| )--#{opt + eq}(?:#{g.join('|')})/
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
ret = []
|
|
376
|
+
target.each do |opt|
|
|
377
|
+
args.each do |pat|
|
|
378
|
+
next unless opt =~ pat
|
|
379
|
+
|
|
380
|
+
ret << ($1 || $2 || $3)
|
|
381
|
+
break
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
return ret unless first || last
|
|
385
|
+
|
|
386
|
+
if last.is_a?(Numeric)
|
|
387
|
+
ret.last(last)
|
|
388
|
+
elsif last
|
|
389
|
+
ret.last
|
|
390
|
+
else
|
|
391
|
+
first.is_a?(Numeric) ? ret.first(first) : ret.first
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def uniq(list)
|
|
396
|
+
ignore = map { |val| nameonly(val) }
|
|
397
|
+
list.reject { |val| ignore.include?(s = nameonly(val)) || any?(OptionPartition.send(:matchopt, s)) }
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def clear(opts = nil, errors: false, **kwargs)
|
|
401
|
+
styles = project.theme[:inline] if project
|
|
402
|
+
if errors
|
|
403
|
+
OptionPartition.clear(target, @errors, styles: styles, **kwargs)
|
|
404
|
+
@errors.clear
|
|
405
|
+
return self unless opts
|
|
406
|
+
end
|
|
407
|
+
opts ||= extras
|
|
408
|
+
OptionPartition.clear(target, opts - found, styles: styles, **kwargs)
|
|
409
|
+
opts.clear
|
|
410
|
+
self
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def adjoin(*args, with: nil, start: false)
|
|
414
|
+
index = -1
|
|
415
|
+
temp = compact
|
|
416
|
+
if with
|
|
417
|
+
pat = case with
|
|
418
|
+
when String, Symbol
|
|
419
|
+
/\A#{Regexp.escape(with)}\z/
|
|
420
|
+
when Array
|
|
421
|
+
OptionPartition.send(:matchopts, with)
|
|
422
|
+
else
|
|
423
|
+
with
|
|
424
|
+
end
|
|
425
|
+
temp.each_with_index do |val, i|
|
|
426
|
+
if val.to_s.match?(pat)
|
|
427
|
+
index = i + (start.is_a?(Numeric) ? start : 1)
|
|
428
|
+
break
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
else
|
|
432
|
+
temp.each_with_index do |val, i|
|
|
433
|
+
if index == 0
|
|
434
|
+
next unless val.is_a?(String) && val.start_with?('-')
|
|
435
|
+
|
|
436
|
+
index = i
|
|
437
|
+
break
|
|
438
|
+
elsif i > 0 && !val.to_s.start_with?('-')
|
|
439
|
+
if start
|
|
440
|
+
index = i + (start.is_a?(Numeric) ? start : 1)
|
|
441
|
+
break
|
|
442
|
+
end
|
|
443
|
+
index = 0
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
if index > 0
|
|
448
|
+
if args.empty?
|
|
449
|
+
args = dup
|
|
450
|
+
reset
|
|
451
|
+
else
|
|
452
|
+
args.each { |val| remove val }
|
|
453
|
+
end
|
|
454
|
+
args = temp[0...index] + args + temp[index..-1]
|
|
455
|
+
target.clear
|
|
456
|
+
end
|
|
457
|
+
merge args
|
|
458
|
+
self
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def add_path(*args, force: true, double: false, **kwargs)
|
|
462
|
+
if args.empty?
|
|
463
|
+
args = select { |val| val.is_a?(String) }
|
|
464
|
+
args.map! { |val| path + val } if path
|
|
465
|
+
append(args, force: force, **kwargs)
|
|
466
|
+
else
|
|
467
|
+
add shell_quote(path ? path.join(*args) : File.join(*args), option: false, force: force, double: double)
|
|
468
|
+
end
|
|
469
|
+
self
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def add_quote(*args, **kwargs)
|
|
473
|
+
merge(args.compact
|
|
474
|
+
.map! { |val| val == '--' || OptionPartition.opt?(val) ? val : shell_quote(val, **kwargs) })
|
|
475
|
+
self
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def add_option(flag, val = nil, **kwargs)
|
|
479
|
+
add shell_option(flag, val, **kwargs)
|
|
480
|
+
self
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def add_first(fallback = nil, prefix: nil, path: false, quote: false, reverse: false, expect: false, **kwargs)
|
|
484
|
+
val = (reverse ? pop : shift) || fallback
|
|
485
|
+
if val
|
|
486
|
+
val.delete_prefix!(prefix) if prefix && val.is_a?(String)
|
|
487
|
+
unless block_given? && !(val = yield val).is_a?(String)
|
|
488
|
+
if path
|
|
489
|
+
add_path(val, **kwargs)
|
|
490
|
+
elsif quote
|
|
491
|
+
add_quote(val, **kwargs)
|
|
492
|
+
else
|
|
493
|
+
add val
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
elsif expect
|
|
497
|
+
raise(expect.is_a?(String) ? expect : 'no value to add')
|
|
498
|
+
end
|
|
499
|
+
self
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def last(val = nil, &blk)
|
|
503
|
+
unless block_given?
|
|
504
|
+
case val
|
|
505
|
+
when NilClass
|
|
506
|
+
return extras.last
|
|
507
|
+
when Numeric
|
|
508
|
+
return extras.last(val)
|
|
509
|
+
when String, Array, Regexp
|
|
510
|
+
val = OptionPartition.send(:matchopts, val) unless val.is_a?(Regexp)
|
|
511
|
+
blk = proc { |s| s&.match?(val) }
|
|
512
|
+
else
|
|
513
|
+
raise TypeError, "unknown: #{val}"
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
ret = find_all(&blk)
|
|
517
|
+
unless ret.empty?
|
|
518
|
+
ret = case val
|
|
519
|
+
when NilClass
|
|
520
|
+
ret.first(1)
|
|
521
|
+
when Numeric
|
|
522
|
+
ret.first(val)
|
|
523
|
+
else
|
|
524
|
+
ret
|
|
525
|
+
end
|
|
526
|
+
ret.each do |opt|
|
|
527
|
+
delete opt
|
|
528
|
+
add opt
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
val.nil? ? ret.first : ret
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
def splice(*exclude, quote: true, delim: true, path: false, pattern: false, &blk)
|
|
535
|
+
found, other = if block_given?
|
|
536
|
+
partition(&blk)
|
|
537
|
+
elsif exclude.first.is_a?(Symbol)
|
|
538
|
+
partition(&exclude.first)
|
|
539
|
+
else
|
|
540
|
+
partition do |val|
|
|
541
|
+
next false if pattern && OptionPartition.pattern?(val)
|
|
542
|
+
|
|
543
|
+
exclude.none? { |pat| val.match?(Regexp.new(pat)) }
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
unless found.empty?
|
|
547
|
+
add '--' if delim
|
|
548
|
+
extras.clear
|
|
549
|
+
concat other
|
|
550
|
+
if path
|
|
551
|
+
found.each { |val| add_path(val) }
|
|
552
|
+
else
|
|
553
|
+
found.map! { |val| shell_quote(val) } if quote
|
|
554
|
+
merge found
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
self
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def reset(errors: false)
|
|
561
|
+
extras.clear
|
|
562
|
+
found.clear
|
|
563
|
+
clear(errors: true) if errors
|
|
564
|
+
self
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def append?(key, val = nil, type: nil, force: false, sep: '=', **kwargs)
|
|
568
|
+
return false unless force || !arg?(key)
|
|
569
|
+
|
|
570
|
+
val = yield self if block_given?
|
|
571
|
+
return false unless val
|
|
572
|
+
|
|
573
|
+
type ||= :quote if kwargs.empty?
|
|
574
|
+
add case type
|
|
575
|
+
when :quote
|
|
576
|
+
quote_option(key, val, sep: sep)
|
|
577
|
+
when :basic
|
|
578
|
+
basic_option(key, val, sep: sep)
|
|
579
|
+
else
|
|
580
|
+
shell_option(key, val, sep: sep, **kwargs)
|
|
581
|
+
end
|
|
582
|
+
true
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
def arg?(*args, **kwargs)
|
|
586
|
+
OptionPartition.arg?(target, *args, **kwargs)
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def exist?(*args, add: false, first: false, last: false, glob: false)
|
|
590
|
+
return with_glob?(File.join(*args), glob) unless args.empty?
|
|
591
|
+
|
|
592
|
+
if first || last
|
|
593
|
+
return false unless (val = first ? self.first : self.last)
|
|
594
|
+
|
|
595
|
+
with_glob?(val, glob).tap do |ret|
|
|
596
|
+
next unless add && ret
|
|
597
|
+
|
|
598
|
+
add_first(path: true, reverse: !first)
|
|
599
|
+
end
|
|
600
|
+
else
|
|
601
|
+
each_with_index do |val, i|
|
|
602
|
+
next unless with_glob?(val, glob)
|
|
603
|
+
|
|
604
|
+
if add
|
|
605
|
+
remove_at i
|
|
606
|
+
add_path val
|
|
607
|
+
end
|
|
608
|
+
return true
|
|
609
|
+
end
|
|
610
|
+
false
|
|
611
|
+
end
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
def uniq!(list)
|
|
615
|
+
unless (list = uniq(list)).empty?
|
|
616
|
+
concat list
|
|
617
|
+
self
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
private
|
|
622
|
+
|
|
623
|
+
def nameonly(val)
|
|
624
|
+
val[OPT_VALUE, 1] || val
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def with_glob?(val, glob = true)
|
|
628
|
+
return false unless path && val.is_a?(String) && !val.empty?
|
|
629
|
+
|
|
630
|
+
path.join(val).exist? || (glob && !path.glob(val).empty?)
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
def windows?
|
|
634
|
+
require 'rake'
|
|
635
|
+
Rake::Win32.windows?
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
end
|
|
641
|
+
end
|