squared 0.4.26 → 0.4.28

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31cca2889400b4e4b51df7249ac426d782a9905cc69e731d0766ef1012002651
4
- data.tar.gz: 4353d4d765dc14916e6db6c9605cc0b02fe48b353adc0ed274c08f01e0cc6f25
3
+ metadata.gz: 2a433a8034032658be9c9ce5068904a993c964f01d63ca4ec4a017a742a6802e
4
+ data.tar.gz: 805ef86bf61d4da1854a766402377b67cc129dca8537a87ffea966019516f17b
5
5
  SHA512:
6
- metadata.gz: 6fda0932de949193edfc82bb533c3b21b39624b2d1d8fc3b1762b0c8e93ce47807e9a34c8b378896d6f4f92c18d06ae82b217dfd42539667cb0df3de5d98cd5c
7
- data.tar.gz: 411bba4daeb35c641b67306bba13502a78cab65c09e94d3a5fcef0f7ad213f5a8a75886b641a32d4e1dce83b6c697784ca438d4c570a30dd3d331ae8b25541c0
6
+ metadata.gz: c7e2e78c6040f77f509419580938f3b501171bded0454eff7b3e2fb9317dacd402cb610b76d513399af23c05c935ad1cd4baffaaa1c601058d808881bb606774
7
+ data.tar.gz: b2c810eccf3fb6cdb614dad59ee846dafb13f223ab8b37b612ab0eacdc443f2383679905e697ffc54415355eafcad71e81586f635618960d6414904361ada790
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.28] - 2025-11-08
4
+
5
+ ### Fixed
6
+
7
+ - Project base method build as arrays parsed unrelated arguments.
8
+ - OptionPartition method arg? does not check after "--" delimiter.
9
+ - OptionPartition did not support duplicate values after "--" delimiter.
10
+ - OptionPartition method opt? did not type check for String.
11
+
12
+ ## [0.4.27] - 2025-11-05
13
+
14
+ ### Changed
15
+
16
+ - OptionPartition does not add quotes when an option flag is detected.
17
+ - Common shell method argument option only parses options with values.
18
+ - Shell options with empty flags are treated as quoted strings.
19
+
20
+ ### Fixed
21
+
22
+ - OptionPartition did not detect flags with middle dashes.
23
+
3
24
  ## [0.4.26] - 2025-10-31
4
25
 
5
26
  ### Added
@@ -1064,6 +1085,8 @@
1064
1085
 
1065
1086
  - Changelog was created.
1066
1087
 
1088
+ [0.4.28]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.28
1089
+ [0.4.27]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.27
1067
1090
  [0.4.26]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.26
1068
1091
  [0.4.25]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.25
1069
1092
  [0.4.24]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.24
@@ -38,11 +38,16 @@ module Squared
38
38
  super[/[^:]+\z/, 0]
39
39
  end
40
40
 
41
- attr_reader :delim
41
+ alias to_ary to_a
42
42
 
43
- def initialize(data = [], delim: ' ')
44
- super(data.compact)
43
+ attr_reader :delim, :extras
44
+
45
+ def initialize(data = [], delim: ' ', partition: '--', uniq: /\A--?[^\[\]=\s-][^\[\]=\s]*(?:=|\s+)\S/)
45
46
  @delim = delim
47
+ @partition = partition
48
+ @uniq = uniq
49
+ @extras = []
50
+ super(data.compact)
46
51
  end
47
52
 
48
53
  def last(val, pat)
@@ -51,7 +56,7 @@ module Squared
51
56
  end
52
57
 
53
58
  def pass(&blk)
54
- ret = to_a.map!(&:to_s).reject(&:empty?)
59
+ ret = to_ary.map!(&:to_s).reject(&:empty?)
55
60
  @last&.each do |val, pat, key|
56
61
  i = []
57
62
  j = nil
@@ -72,6 +77,7 @@ module Squared
72
77
  end
73
78
  ret[i.last] = val
74
79
  end
80
+ ret.concat(extras.map(&:to_s).reject(&:empty?)) unless extras.empty?
75
81
  block_given? ? ret.reject(&blk) : ret
76
82
  end
77
83
 
@@ -91,9 +97,9 @@ module Squared
91
97
 
92
98
  def temp(*args, &blk)
93
99
  args.compact!
94
- ret = pass(&blk)
95
- ret = Set.new(ret.concat(args)).to_a unless args.empty?
96
- ret.join(@delim)
100
+ pass(&blk)
101
+ .concat(args)
102
+ .join(@delim)
97
103
  end
98
104
 
99
105
  def done
@@ -102,9 +108,64 @@ module Squared
102
108
  ret
103
109
  end
104
110
 
111
+ def merge(enum)
112
+ if !extras.empty?
113
+ extras.concat(enum.to_a)
114
+ self
115
+ elsif (n = enum.find_index { |val| extras?(val) })
116
+ data = enum.to_a
117
+ @extras = if n == 0
118
+ data
119
+ else
120
+ super(data[0...n])
121
+ data[n..-1]
122
+ end
123
+ self
124
+ else
125
+ super
126
+ end
127
+ end
128
+
129
+ def <<(obj)
130
+ extras!(obj) || super
131
+ end
132
+
133
+ def add?(obj)
134
+ extras!(obj) || super
135
+ end
136
+
137
+ def to_a
138
+ pass
139
+ end
140
+
105
141
  def to_s
106
142
  pass.join(@delim)
107
143
  end
144
+
145
+ def to_enum(*args)
146
+ pass.to_enum(*args)
147
+ end
148
+
149
+ def to_json(*args)
150
+ pass.to_json(*args)
151
+ end
152
+
153
+ def to_yaml(*args)
154
+ pass.to_yaml(*args)
155
+ end
156
+
157
+ private
158
+
159
+ def extras!(obj)
160
+ return if extras.empty? && !extras?(obj)
161
+
162
+ extras << obj unless !extras.include?(@partition) && include?(obj) && @uniq.match?(obj.to_s)
163
+ self
164
+ end
165
+
166
+ def extras?(obj)
167
+ obj == @partition || (include?(obj) && !@uniq.match?(obj.to_s))
168
+ end
108
169
  end
109
170
  end
110
171
  end
@@ -253,8 +253,8 @@ module Squared
253
253
  sub.each { |h| s = sub_style(s, **h) }
254
254
  s = "#{b0} #{s} #{b0}"
255
255
  if border
256
- s = sub_style(s, pat: /\A(#{Regexp.escape(b0)})(.+)\z/m, styles: border)
257
- s = sub_style(s, pat: /\A(.+)(#{Regexp.escape(b0)})\z/m, styles: border, index: 2)
256
+ s = sub_style(s, pat: /\A(#{Regexp.escape(b0)})(.+)\z/om, styles: border)
257
+ s = sub_style(s, pat: /\A(.+)(#{Regexp.escape(b0)})\z/om, styles: border, index: 2)
258
258
  end
259
259
  s
260
260
  end
@@ -6,11 +6,14 @@ require 'rake'
6
6
  module Squared
7
7
  module Common
8
8
  module Shell
9
+ QUOTE_VALUE = /\A(["'])(.*)\1\z/m.freeze
10
+ private_constant :QUOTE_VALUE
11
+
9
12
  module_function
10
13
 
11
14
  def shell_escape(val, quote: false, force: false, double: false, option: false, override: false)
12
- if (r = /\A(--?)([^= ]+)((=|\s+)(["'])?(?(5)(.*)\5|(.*)))?\z/m.match(val = val.to_s))
13
- if (data = r[2].match(/\A(["'])(.+)\1\z/))
15
+ if (r = /\A(--?)([^=\s]+)((=|\s+)(["'])?(?(5)(.*)\5|(.*)))?\z/m.match(val = val.to_s))
16
+ if (data = r[2].match(QUOTE_VALUE))
14
17
  double = data[1] == '"'
15
18
  override = true
16
19
  elsif !r[3] || r[6]
@@ -25,9 +28,9 @@ module Squared
25
28
 
26
29
  r[7]
27
30
  end
28
- r[1] + (data ? data[2] : r[2]) + r[4] + shell_quote(opt, double: double, force: force, override: override)
29
- elsif option && val =~ /\A([^=]+)=(.+)\z/m
30
- return val if $2.match?(/\A(["']).+\1\z/m)
31
+ r[1] + (data ? data[2] : r[2]) + r[4] + shell_quote(opt, force: force, double: double, override: override)
32
+ elsif option && val =~ /\A(-{0,2}[^\[\]=\s-][^\[\]=\s]*)=(.+)\z/m
33
+ return val if $2.match?(QUOTE_VALUE)
31
34
 
32
35
  "#{$1}=%s" % if $2.include?(' ')
33
36
  shell_quote($2, option: false)
@@ -35,54 +38,64 @@ module Squared
35
38
  Rake::Win32.windows? ? $2 : Shellwords.escape($2)
36
39
  end
37
40
  elsif Rake::Win32.windows?
38
- quote ? shell_quote(val, double: double, force: force) : val
41
+ quote ? shell_quote(val, force: force, double: double) : val
42
+ elsif val.empty?
43
+ ''
39
44
  else
40
- val.empty? ? '' : Shellwords.escape(val)
45
+ Shellwords.escape(val)
41
46
  end
42
47
  end
43
48
 
44
- def shell_quote(val, option: true, force: true, double: false, override: false)
49
+ def shell_quote(val, option: true, force: true, double: false, preserve: true, override: false)
45
50
  val = val.to_s
46
51
  return val if (!force && !val.include?(' ')) || val.empty?
47
52
 
48
- if option && val.match?(/(?:\A|\A[^=\s]+(?:=|\s+)|#{Rake::Win32.windows? ? '[\\\/]' : '\/'})(["']).+\1\z/m)
49
- return val
53
+ if option
54
+ pat = /\A(?:-[^\[\]=\s-](?:=|\s+)?|(--)?[^\[\]=\s-][^\[\]=\s]*(?(1)(?:=|\s+)|=))(["']).+\2\z/m
55
+ return val if val.match?(pat)
50
56
  end
57
+ q = ->(s) { s.gsub("'\\\\''", "'") }
58
+ if val =~ QUOTE_VALUE
59
+ return val if $1 == '"' && Rake::Win32.windows? && val.match?(/(?:[#{File::SEPARATOR} ]|\\")/o)
51
60
 
61
+ base = $2 unless preserve
62
+ end
52
63
  if double || Rake::Win32.windows? || (ARG[:QUOTE] == '"' && !override)
53
- "\"#{val.gsub(/(?<!\\)"/, '\\"')}\""
64
+ "\"#{q.call(base || val).gsub(/(?<!\\)"/, '\\"')}\""
54
65
  else
55
- "'#{val.gsub("'", "'\\\\''")}'"
66
+ base ? val : "'#{q.call(val).gsub("'", "'\\\\''")}'"
56
67
  end
57
68
  end
58
69
 
59
70
  def shell_option(flag, val = nil, escape: true, quote: true, option: true, force: true, double: false,
60
71
  merge: false, override: false)
61
72
  flag = flag.to_s
62
- if flag =~ /\A(["'])(.+)\1\z/
73
+ if flag =~ QUOTE_VALUE
63
74
  double = $1 == '"'
64
75
  flag = $2
65
76
  escape = false
66
77
  override = true
67
78
  end
68
- b = if flag[0] == '-'
69
- flag[1] == '-' ? '=' : ' '
70
- elsif flag.size == 1
71
- a = '-'
72
- merge ? '' : ' '
73
- else
74
- a = '--'
75
- '='
76
- end
77
- "#{a}#{flag}#{unless val.nil?
78
- "#{b}#{if escape
79
- shell_escape(val, quote: quote, double: double, override: override)
80
- elsif quote
81
- shell_quote(val, option: option, force: force, double: double, override: override)
82
- else
83
- val
84
- end}"
85
- end}"
79
+ sep = unless flag.empty?
80
+ if flag[0] == '-'
81
+ flag[1] == '-' ? '=' : ' '
82
+ elsif flag.size == 1
83
+ pre = '-'
84
+ merge ? '' : ' '
85
+ else
86
+ pre = '--'
87
+ '='
88
+ end
89
+ end
90
+ "#{pre}#{flag}#{unless val.nil?
91
+ "#{sep}#{if escape
92
+ shell_escape(val, quote: quote, double: double, override: override)
93
+ elsif quote
94
+ shell_quote(val, option: option, force: force, double: double, override: override)
95
+ else
96
+ val
97
+ end}"
98
+ end}"
86
99
  end
87
100
 
88
101
  def shell_split(val, quote: false, force: false, join: nil)
@@ -94,8 +107,8 @@ module Squared
94
107
 
95
108
  def shell_bin(name, env: true)
96
109
  key = name.upcase
97
- shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name, option: false, force: false,
98
- double: true)
110
+ shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name,
111
+ option: false, force: false, double: true)
99
112
  end
100
113
 
101
114
  def fill_option(val, **kwargs)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.4.26'
4
+ VERSION = '0.4.28'
5
5
  end
@@ -482,31 +482,31 @@ module Squared
482
482
  cmd = cmd.join(' && ')
483
483
  else
484
484
  cmd, opts, var, flags, extra = args
485
- end
486
- if cmd
487
- return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
485
+ if cmd
486
+ return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
488
487
 
489
- cmd = as_get(cmd, from)
490
- opts = compose(opts, script: false) if opts && respond_to?(:compose)
491
- flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
492
- case opts
493
- when Hash
494
- cmd = Array(cmd).push(flags)
495
- .concat(append_hash(opts, target: [], build: true))
496
- .compact
497
- .join(' ')
498
- when Enumerable
499
- cmd = Array(cmd).concat(opts.to_a)
500
- cmd.map! { |val| "#{val} #{flags}" } if flags
501
- cmd = cmd.join(' && ')
488
+ cmd = as_get(cmd, from)
489
+ opts = compose(opts, script: false) if opts && respond_to?(:compose)
490
+ flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
491
+ case opts
492
+ when Hash
493
+ cmd = Array(cmd).push(flags)
494
+ .concat(append_hash(opts, target: [], build: true))
495
+ .compact
496
+ .join(' ')
497
+ when Enumerable
498
+ cmd = Array(cmd).concat(opts.to_a)
499
+ cmd.map! { |val| "#{val} #{flags}" } if flags
500
+ cmd = cmd.join(' && ')
501
+ else
502
+ cmd = [cmd, flags, opts].compact.join(' ') if opts || flags
503
+ end
502
504
  else
503
- cmd = [cmd, flags, opts].compact.join(' ') if opts || flags
504
- end
505
- else
506
- return unless (opts || extra) && respond_to?(:compose)
505
+ return unless (opts || extra) && respond_to?(:compose)
507
506
 
508
- cmd = compose(as_get(opts, from), flags, script: true, args: extra, from: from)
509
- from = :script if from == :run && script?
507
+ cmd = compose(as_get(opts, from), flags, script: true, args: extra, from: from)
508
+ from = :script if from == :run && script?
509
+ end
510
510
  end
511
511
  run(cmd, var, sync: sync, from: from, banner: banner)
512
512
  end
@@ -525,12 +525,12 @@ module Squared
525
525
  if proj.respond_to?(meth.to_sym)
526
526
  begin
527
527
  proj.__send__(meth, sync: sync)
528
- next
529
528
  rescue StandardError => e
530
529
  on_error(e, :prereqs, exception: true)
531
530
  end
531
+ else
532
+ print_error(name, "method: #{meth}", subject: 'prereqs', hint: 'undefined')
532
533
  end
533
- print_error(name, 'method not found', subject: 'prereqs', hint: meth)
534
534
  end
535
535
  elsif proj.build?
536
536
  proj.build(sync: sync)
@@ -697,7 +697,7 @@ module Squared
697
697
  end
698
698
  break uri = url if data
699
699
  end
700
- unless data && (ext ||= URI.parse(uri).path[/\.(\w+)(?:\?|\z)/, 1])
700
+ unless data && (ext ||= URI.decode_www_form_component(URI.parse(uri).path[/\.([\w%]+)(?:\?|\z)/, 1]))
701
701
  raise_error("no content#{data ? ' type' : ''}", hint: uri)
702
702
  end
703
703
  end
@@ -1237,7 +1237,7 @@ module Squared
1237
1237
  end
1238
1238
  ret = JoinSet.new(cmd.flatten(1))
1239
1239
  if options && (val = env("#{prefix.upcase}_OPTIONS"))
1240
- split_escape(val).each { |opt| ret.last(fill_option(opt), /\A(--?[^ =]+)[ =].+\z/m) }
1240
+ split_escape(val).each { |opt| ret.last(fill_option(opt), /\A(--?[^\[\]=\s-][^\[\]=\s]*)[=\s].+\z/m) }
1241
1241
  end
1242
1242
  main ? @session = ret : ret
1243
1243
  end
@@ -1527,11 +1527,12 @@ module Squared
1527
1527
  **kwargs)
1528
1528
  return if list.empty?
1529
1529
 
1530
+ kwargs[:ignore] = false if no && !kwargs.key?(:ignore)
1530
1531
  ret = []
1531
1532
  list.flatten.each do |flag|
1532
1533
  next unless (val = option(flag, target: target, **kwargs))
1533
1534
 
1534
- if val == '0' && no
1535
+ if no && val == '0'
1535
1536
  flag = "no-#{flag}"
1536
1537
  val = nil
1537
1538
  end
@@ -10,9 +10,9 @@ module Squared
10
10
  include Common::Shell
11
11
  extend Forwardable
12
12
 
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/
13
+ OPT_NAME = /\A(?:(--)|-)((?(1)[^\[\]=\s-][^\[\]=\s]*|[^\[\]=\s-]))\z/
14
+ OPT_VALUE = /\A-{0,2}([^\[\]=\s-][^\[\]=\s]*)(?:\s*=\s*|\s+)(.+)\z/
15
+ OPT_SINGLE = /\A-([^\[\]=\s-])(.+)\z/
16
16
  private_constant :OPT_NAME, :OPT_VALUE, :OPT_SINGLE
17
17
 
18
18
  class << self
@@ -27,11 +27,13 @@ module Squared
27
27
  target << '--' if delim && !target.include?('--')
28
28
  if strip
29
29
  pat, s = Array(strip)
30
- ret.map! { |val| val.gsub(pat, s || '') }
30
+ ret.map! { |val| val.is_a?(String) ? val.gsub(pat, s || '') : val }
31
31
  end
32
32
  if escape || quote
33
33
  ret.map! do |val|
34
- if escape
34
+ if opt?(val)
35
+ val
36
+ elsif escape
35
37
  shell_escape(val, quote: quote, double: double)
36
38
  else
37
39
  shell_quote(val, force: force, double: double)
@@ -87,7 +89,17 @@ module Squared
87
89
  def arg?(target, *args, value: false, **)
88
90
  r, s = args.partition { |val| val.is_a?(Regexp) }
89
91
  r << matchopts(s, value: value) unless s.empty?
90
- Array(target).compact.any? { |val| r.any? { |pat| pat.match?(val.to_s) } }
92
+ a = target.to_a.compact
93
+ if (n = a.index('--'))
94
+ a = a[0..n]
95
+ end
96
+ a.any? { |val| r.any? { |pat| pat.match?(val.to_s) } }
97
+ end
98
+
99
+ def opt?(val)
100
+ return false unless val.is_a?(String)
101
+
102
+ val.start_with?('-') && (OPT_NAME.match?(val) || OPT_VALUE.match?(val) || OPT_SINGLE.match?(val))
91
103
  end
92
104
 
93
105
  def pattern?(val)
@@ -380,7 +392,8 @@ module Squared
380
392
  end
381
393
 
382
394
  def add_quote(*args, **kwargs)
383
- merge(args.map! { |val| shell_quote(val, **kwargs) })
395
+ args.compact!
396
+ merge(args.map! { |val| val == '--' || OptionPartition.opt?(val) ? val : shell_quote(val, **kwargs) })
384
397
  self
385
398
  end
386
399
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.26
4
+ version: 0.4.28
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
@@ -125,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubygems_version: 3.7.2
128
+ rubygems_version: 3.6.9
129
129
  specification_version: 4
130
130
  summary: Rake task generator for managing multi-language workspaces.
131
131
  test_files: []