squared 0.6.2 → 0.6.4

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: c638ae780897008e5115630f70db5a8ce7e1930fd31e7d1984b16d775218e16b
4
- data.tar.gz: 231f5fc087b0b3dfcff0289f5601b0c70f0a9e6db63ff7309b7c86d96ae0a1ef
3
+ metadata.gz: f66aceeba825bcfd6afd3b20e5c39431d94450639cc70e07c3c8c22725426595
4
+ data.tar.gz: b23a0282dbe08d322ed19931ddaa59be93c4b5caeb603fb90ca4283a0f195bb0
5
5
  SHA512:
6
- metadata.gz: 175e72f4f5821922928b2b95a7f831dc42e91bbc41f093686cd253a68794b4b34db8af2bb9d103871cd19dfdf4d21ddd1d2bf30efdd8a8b4f5a7475be77b9988
7
- data.tar.gz: 7c619485cf6d8046ba0e2acd99afcb698137e6342fd68b454c1195f9f32517d133aada79783813c15978eb1af132113aa94c422d425ae76b36a033345c81a4e3
6
+ metadata.gz: ffa105272b8235dd427597c590d372d910edc40e2cd988dfe6b779355c2f036f32fdf6ec909d336cb84a7bf38db425e456a982b51cfad4d4fa499cb583382f27
7
+ data.tar.gz: cb767f0c63b833b4550199caf2d0fc2f2d98bda119e4f0c128de5e7083fe6da7813402a46aaabfabb0b19dd56c23f77d7732167673df2bf50996ca47c55d9fa0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,73 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.4] - 2025-11-16
4
+
5
+ ### Added
6
+
7
+ - Python command outdated can hide non-upgradable packages.
8
+ - Ruby command outdated option dry-run was implemented.
9
+ - Ruby command outdated option interactive was implemented.
10
+ - Repo binary can be downloaded and run from any local directory.
11
+ - Python command outdated option dry-run was implemented.
12
+ - Common shell defined Array instance method quote!.
13
+ - Python command outdated option interactive was implemented.
14
+
15
+ ### Changed
16
+
17
+ - Gem command outdated uses inline interactive prompts.
18
+ - Log messages with level INFO can be called without level argument.
19
+ - Project base attribute project was converted into an accessor.
20
+
21
+ ### Fixed
22
+
23
+ - Node command tsc did not accept a tsconfig or watch argument.
24
+ - Repo application tasks are not created on Windows.
25
+ - Repo module used conflicting REPO_URL with Repo application.
26
+ - Project task outdated did not check pass and only exclusions.
27
+ - Python command install did not include available options.
28
+ - Workspace static method resolve did nothing when given a String.
29
+ - Project base run command types did not include Struct.
30
+
31
+ ## [0.6.3] - 2025-11-14
32
+
33
+ ### Added
34
+
35
+ - Node command package action rebuild was implemented.
36
+ - Node command add can remove packages using "-" prefix.
37
+ - Git command log action grep was implemented.
38
+ - Node command package action add was implemented.
39
+
40
+ ### Changed
41
+
42
+ - Application class extended Enumerable collection class.
43
+ - OptionPartition class is lazily loaded using autoload.
44
+ - Requiring individual project classes in Rakefile is optional.
45
+ - Application class uses static method register instead of series_wrap.
46
+ - Project support modules are required inside Base class module.
47
+
48
+ ### Fixed
49
+
50
+ - Project graph print did not display parent and last child consequetively.
51
+ - Project outdated interactive prompts use exact column alignments.
52
+
53
+ ## [0.5.16] - 2025-11-14
54
+
55
+ ### Fixed
56
+
57
+ - See `0.4.30`.
58
+
59
+ ## [0.4.30] - 2025-11-14
60
+
61
+ ### Added
62
+
63
+ - Config viewer can read items by index in an Array.
64
+
65
+ ### Fixed
66
+
67
+ - Node command add uses event name "add" and not "depend".
68
+ - Node command add did not include packages with Yarn and PNPM.
69
+ - Git method revbuild did not splat build arguments.
70
+
3
71
  ## [0.6.2] - 2025-11-08
4
72
 
5
73
  ### Added
@@ -1351,9 +1419,12 @@
1351
1419
 
1352
1420
  - Changelog was created.
1353
1421
 
1422
+ [0.6.4]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.4
1423
+ [0.6.3]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.3
1354
1424
  [0.6.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.2
1355
1425
  [0.6.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.1
1356
1426
  [0.6.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.0
1427
+ [0.5.16]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.16
1357
1428
  [0.5.15]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.15
1358
1429
  [0.5.14]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.14
1359
1430
  [0.5.13]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.13
@@ -1370,6 +1441,7 @@
1370
1441
  [0.5.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.2-ruby
1371
1442
  [0.5.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.1-ruby
1372
1443
  [0.5.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.0-ruby
1444
+ [0.4.30]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.30
1373
1445
  [0.4.29]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.29
1374
1446
  [0.4.28]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.28
1375
1447
  [0.4.27]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.27
data/README.md CHANGED
@@ -75,10 +75,14 @@ require "squared/app" # All workspace related mod
75
75
  # sqd-serve = /workspaces/squared/publish/sqd-serve
76
76
  # sqd = /workspaces/squared/sqd
77
77
 
78
+ # Load external Project classes (optional)
79
+ Workspace::Application.load_ref('/home/user/.gem/ruby/3.4.0/gems/squared-0.6.0/lib/squared/workspace/project') # ref = :node => node.rb (absolute)
80
+ Workspace::Application.load_ref('lib/squared/workspace/project', gem: 'squared') # bundle env (relative)
81
+
78
82
  Workspace::Application
79
83
  .new(Dir.pwd, main: "squared") # Dir.pwd? (main? is implicitly basename)
80
84
  .banner("group", "project", styles: ["yellow", "black"], border: "bold") # name | project | path | ref | group? | parent? | version?
81
- .repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "build:prod"], ref: :node) # Repo (optional)
85
+ .repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "prod"], install: "#{ENV["HOME"]}/.bin", ref: :node) # Repo (optional)
82
86
  .run("rake install", ref: :ruby)
83
87
  .depend(false, group: "default")
84
88
  .clean("rake clean", group: "default")
@@ -150,13 +154,13 @@ Node = Workspace::Project::Node # tsc
150
154
  Node.options("build:dev", "target=es2022", project: "tsconfig.json") # :node (ref)
151
155
  Node.options("build:dev", "outDir=tmp", :squared) # squared (project name)
152
156
 
153
- Ruby = Workspace::Project::Ruby # ruby | gem | rake | bundle | irb
157
+ Ruby = Workspace::Project::Ruby # ruby | gem | rake | bundle | irb | rbs
154
158
  Ruby.options("lint", "rubocop", opts: ["gemfile=Gemfile"]) # :ruby
155
159
  Ruby.options("lint", "--parallel", :squared)
156
160
 
157
- Python = Workspace::Project::Python # pip
158
- Python.options("build", opts: ["r=requirements.txt"]) # :python
159
- Python.options(:build, "build:dev", opts: ["no-deps"])
161
+ Python = Workspace::Project::Python # pip
162
+ Python.options("build", opts: ["r=requirements.txt"]) # :python
163
+ Python.options(:build, "build:dev", opts: ["no-deps"]) # inherits "build" as "build:dev"
160
164
 
161
165
  Workspace::Application
162
166
  .new(ENV["SQUARED_HOME"], prefix: "rb", common: false) # Local styles
@@ -692,7 +696,8 @@ GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
692
696
  | checkout | track | COUNT=n |
693
697
  | checkout | global path | HEAD=s PATHSPEC=s |
694
698
  | checkout | * | F|FORCE MERGE |
695
- | clone | * | DEPTH=n ORIGIN=s BRANCH=s REVISION=s LOCAL=0,1 |
699
+ | clone | * | DEPTH=n ORIGIN=s BRANCH=s REVISION=s BARE=1 LOCAL=0,1 |
700
+ | | | SINGLE_BRANCH=0,1 NO_CHECKOUT=1 NO_TAGS=1 QUIET=1 |
696
701
  | commit | * | UPSTREAM=s DRY_RUN EDIT=0 M|MESSAGE=s |
697
702
  | diff | -between -contain | MERGE_BASE |
698
703
  | diff | head branch | INDEX=n |
@@ -742,14 +747,19 @@ BUILD_SQUARED_OPTS="NODE_TAG=24 RUBY_VERSION=3.4.0" DOCKER_SQUARED_OPTS="--no-ca
742
747
  docker build --no-cache --label=v1 --build-arg='NODE_TAG=24' --build-arg='RUBY_VERSION=3.4.0' .
743
748
  ```
744
749
 
745
- | Command | Flag | ENV |
746
- | :--------- | :---------------- | :---------------------------------------------- |
747
- | buildx | build | TAG=s |
748
- | buildx | bake | SERVICE=s |
749
- | compose | build | TARGET=s |
750
- | container | commit | REGISTRY=s PLATFORM=s DISABLE_CONTENT_TRUST=0,1 |
751
- | image | rm | Y=0,1 |
752
- | image | push | TAG=s REGISTRY=s |
750
+ | Command | Flag | ENV |
751
+ | :--------- | :----------------- | :---------------------------------------------- |
752
+ | buildx | build context | TAG=s |
753
+ | buildx | bake | SERVICE=s |
754
+ | compose | build | TARGET=s |
755
+ | compose | run | VERSION=s |
756
+ | container | commit | REGISTRY=s PLATFORM=s DISABLE_CONTENT_TRUST=0,1 |
757
+ | container | -run -create -exec | ALL=1 |
758
+ | | -update -commit | |
759
+ | image | rm | Y=1 |
760
+ | image | push | TAG=s REGISTRY=s |
761
+ | image | -push | ALL=1 |
762
+ | network | * | ALL=1 |
753
763
 
754
764
  ### asdf
755
765
 
@@ -782,7 +792,7 @@ REPO_DEV # pattern,0,1
782
792
  REPO_PROD # pattern,0,1
783
793
  REPO_WARN # 0,1
784
794
  REPO_SYNC # 0,1
785
- REPO_URL # manifest repository
795
+ REPO_GIT # manifest repository
786
796
  REPO_MANIFEST # e.g. latest,nightly,prod
787
797
  REPO_GROUPS # e.g. base,prod,docs
788
798
  REPO_STAGE # 0,1,2,3,4
@@ -14,8 +14,8 @@ module Squared
14
14
  CHOICE: 25,
15
15
  QUOTE: "'",
16
16
  SPACE: ' => ',
17
- GRAPH: ['|', '-', '|', '\\', '-'].freeze,
18
- BORDER: ['|', '-', '-', '-', '-', '-', '|', '|', '-', '-'].freeze,
17
+ GRAPH: %w[| - | \\ -].freeze,
18
+ BORDER: %w[| - - - - - | | - -].freeze,
19
19
  VIEW: 'view',
20
20
  BACKTRACE: $DEBUG || !$VERBOSE.nil?,
21
21
  LEVEL: ENV.fetch('LOG_LEVEL', 0).to_i,
@@ -28,8 +28,8 @@ module Squared
28
28
  bright_cyan!: '106',
29
29
  bright_white!: '107'
30
30
  }.freeze
31
- BOX_GRAPH = ['', '', '', '', ''].freeze
32
- BOX_BORDER = ['', '', '', '', '', '', '', '', '', ''].tap do |val|
31
+ BOX_GRAPH = %w[│ ─ ├ └ ┬].freeze
32
+ BOX_BORDER = %w[│ ─ ┌ ┐ ┘ └ ├ ┤ ┬ ┴].tap do |val|
33
33
  if ENV['TERM']&.end_with?('256color')
34
34
  val.slice!(2, 4)
35
35
  val.insert(2, '╭', '╮', '╯', '╰')
@@ -61,25 +61,25 @@ module Squared
61
61
  def sub_style(val, *args, styles: nil, pat: nil, index: 1)
62
62
  return val unless ARG[:COLOR]
63
63
 
64
- if pat && index != 0
65
- return val unless (data = pat.match(val))
64
+ ret = if pat && index != 0
65
+ return val unless (data = pat.match(val))
66
66
 
67
- ret = index == -1 ? data.to_a.drop(1) : data[index]
68
- else
69
- ret = val
70
- index = 0
71
- end
67
+ index == -1 ? data.to_a.drop(1) : data[index]
68
+ else
69
+ index = 0
70
+ val
71
+ end
72
72
  wrap = ->(s, n) { "\x1B[#{n.join(';')}m#{s}\x1B[0m" }
73
73
  code = []
74
+ args.clear if args.size == 1 && args.first.nil?
74
75
  args.concat(Array(styles)).flatten.each_with_index do |type, i|
75
76
  next unless type
76
77
 
77
- if index == -1
78
- s = ret[i]
79
- next ret[i] = '' if s.nil?
80
- else
81
- s = ret
82
- end
78
+ s = if index == -1
79
+ ret[i] || (next ret[i] = '')
80
+ else
81
+ ret
82
+ end
83
83
  if type.is_a?(::Numeric)
84
84
  f, b = type.to_s.split('.')
85
85
  s = wrap.call(s, ['38', '5', f]) if f[0] != '-' && f.to_i <= 255
@@ -125,6 +125,10 @@ module Squared
125
125
  out
126
126
  end
127
127
 
128
+ def sub_style!(val, *args, **kwargs)
129
+ val.replace(sub_style(val, *args, **kwargs))
130
+ end
131
+
128
132
  def check_style(args, empty: true)
129
133
  ret = []
130
134
  colors = __get__(:colors)
@@ -185,6 +189,10 @@ module Squared
185
189
  end
186
190
 
187
191
  def log_message(level, *args, subject: nil, hint: nil, append: true, pass: false, color: ARG[:COLOR])
192
+ if args.empty?
193
+ args.concat(Array(level))
194
+ level = Logger::INFO
195
+ end
188
196
  args = args.map(&:to_s)
189
197
  if level.is_a?(::Numeric)
190
198
  if append && respond_to?(:log)
@@ -192,9 +200,9 @@ module Squared
192
200
  ref.add(level, message(subject, *args, hint: hint, space: ', ')) if ref.is_a?(::Logger)
193
201
  end
194
202
  end
195
- return false if !pass && level < ARG[:LEVEL]
203
+ return false unless pass || level >= ARG[:LEVEL]
196
204
  end
197
- if (args.size > 1 && !hint) || hint == false
205
+ if hint.nil? ? args.size > 1 : !hint
198
206
  title = log_title(level, color: false)
199
207
  emphasize(args,
200
208
  title: title + (subject ? " #{subject}" : ''),
@@ -204,7 +212,9 @@ module Squared
204
212
  end)
205
213
  else
206
214
  msg = [log_title(level, color: color)]
207
- msg << (color ? sub_style(subject.to_s, styles: (@theme && @theme[:subject]) || :bold) : subject) if subject
215
+ if subject
216
+ msg << (color ? sub_style(subject.to_s, (@theme.is_a?(::Hash) && @theme[:subject]) || :bold) : subject)
217
+ end
208
218
  msg << args.shift if msg.size == 1
209
219
  message(msg.join(' '), *args, hint: hint)
210
220
  end
@@ -236,7 +246,7 @@ module Squared
236
246
  end
237
247
 
238
248
  def emphasize(val, title: nil, footer: nil, right: false, cols: nil, sub: nil, pipe: nil,
239
- border: @theme && @theme[:border])
249
+ border: @theme.is_a?(::Hash) && @theme[:border])
240
250
  n = 0
241
251
  max = ->(a) { n = [n, a.max_by(&:size).size].max }
242
252
  set = ->(s) { Array(s).map(&:to_s).tap { |a| max.call(a) } }
@@ -257,17 +267,19 @@ module Squared
257
267
  out = []
258
268
  draw = lambda do |a, b|
259
269
  ret = a + (b1 * (n + 2)) + b
260
- ret = sub_style(ret, styles: border) if border
261
- ret
270
+ next ret unless border
271
+
272
+ sub_style ret, border
262
273
  end
263
274
  sub = sub.is_a?(::Hash) ? [sub] : Array(sub)
264
275
  pr = lambda do |line|
265
276
  s = line.ljust(n)
266
- sub.each { |h| s = sub_style(s, **h) }
277
+ sub.each { |h| sub_style!(s, **h) }
267
278
  s = "#{b0} #{s} #{b0}"
268
279
  if border
269
- s = sub_style(s, **opt_style(border, /\A(#{Regexp.escape(b0)})(.+)\z/om))
270
- s = sub_style(s, **opt_style(border, /\A(.+)(#{Regexp.escape(b0)})\z/om, 2))
280
+ [[/\A(#{Regexp.escape(b0)})(.+)\z/om], [/\A(.+)(#{Regexp.escape(b0)})\z/om, 2]].each do |args|
281
+ sub_style!(s, **opt_style(border, *args))
282
+ end
271
283
  end
272
284
  s
273
285
  end
@@ -282,7 +294,7 @@ module Squared
282
294
  unless sub.empty? && !right
283
295
  footer.map! do |s|
284
296
  s = s.rjust(n + 4) if right
285
- sub.each { |h| s = sub_style(s, **h) }
297
+ sub.each { |h| sub_style!(s, **h) }
286
298
  s
287
299
  end
288
300
  end
@@ -291,17 +303,16 @@ module Squared
291
303
  if block_given?
292
304
  yield out
293
305
  elsif pipe
306
+ return out if pipe == -1
307
+
294
308
  case pipe
295
- when -1
296
- return out
297
309
  when 0
298
- pipe = $stdin
310
+ $stdin
299
311
  when 2
300
- pipe = $stderr
312
+ $stderr
301
313
  else
302
- pipe = $stdout unless pipe.respond_to?(:puts)
303
- end
304
- pipe.puts out
314
+ pipe.respond_to?(:puts) ? pipe : $stdout
315
+ end.puts(out)
305
316
  else
306
317
  err ? warn(out) : puts(out)
307
318
  end
@@ -19,7 +19,7 @@ module Squared
19
19
  module Prompt
20
20
  module_function
21
21
 
22
- def confirm(msg, default = nil, agree: 'Y', cancel: 'N', attempts: 3, timeout: 60)
22
+ def confirm(msg, default = nil, agree: 'Y', cancel: 'N', force: false, attempts: 3, timeout: 60)
23
23
  require 'timeout'
24
24
  if agree == 'Y' && cancel == 'N' && !msg.match?(%r{\[(?:Yn|nY|Y/n|y/N)\]})
25
25
  case default
@@ -38,6 +38,7 @@ module Squared
38
38
  when agree
39
39
  return true
40
40
  when cancel
41
+ exit 1 if force
41
42
  return false
42
43
  end
43
44
  attempts -= 1
@@ -47,6 +48,7 @@ module Squared
47
48
  puts
48
49
  exit 0
49
50
  else
51
+ exit 1 if force
50
52
  false
51
53
  end
52
54
  end
@@ -129,7 +131,7 @@ module Squared
129
131
  multiline = if multiline && Readline.respond_to?(:readmultiline)
130
132
  multiline.is_a?(::Enumerable) || block_given? ? multiline : [multiline.to_s]
131
133
  end
132
- prompt = lambda do
134
+ read = lambda do
133
135
  if !multiline
134
136
  Readline.readline(msg, history)
135
137
  elsif block_given?
@@ -145,12 +147,12 @@ module Squared
145
147
  else
146
148
  [force ? ':' : '?', '']
147
149
  end
148
- ret = (prompt.call || '').strip
150
+ ret = (read.call || '').strip
149
151
  multiline.each { |val| break if ret.delete_suffix!(val.to_s) } if multiline.is_a?(::Enumerable)
150
152
  exit 1 if force && ret.empty?
151
153
  ret
152
154
  else
153
- prompt.call
155
+ read.call
154
156
  end
155
157
  end
156
158
  end
@@ -10,10 +10,11 @@ module Squared
10
10
  private_constant :QUOTE_VALUE
11
11
 
12
12
  String.define_method(:stripquote) { sub(QUOTE_VALUE, '\2') }
13
+ Array.define_method(:quote!) { |**kwargs| map! { |s| Shell.shell_quote(s, **kwargs) } }
13
14
 
14
15
  module_function
15
16
 
16
- def shell_escape(val, quote: false, force: false, double: false, option: false, override: false)
17
+ def shell_escape(val, quote: false, option: false, force: false, double: false, override: false)
17
18
  if (r = /\A(--?)([^=\s]+)((=|\s+)(["'])?(?(5)(.*)\5|(.*)))?\z/m.match(val = val.to_s))
18
19
  if (data = r[2].match(QUOTE_VALUE))
19
20
  double = data[1] == '"'
@@ -31,7 +32,7 @@ module Squared
31
32
  r[7]
32
33
  end
33
34
  r[1] + (data ? data[2] : r[2]) + r[4] + shell_quote(opt, force: force, double: double, override: override)
34
- elsif option && val =~ /\A(-{0,2}[^\[\]=\s-][^\[\]=\s]*)=(.+)\z/m
35
+ elsif option && val =~ /\A(-{0,2}[^=\s-][^=\s]*)=(.+)\z/m
35
36
  return val if $2.match?(QUOTE_VALUE)
36
37
 
37
38
  "#{$1}=%s" % if $2.include?(' ')
@@ -55,7 +56,7 @@ module Squared
55
56
  return val if (!force && !val.include?(' ')) || val.empty?
56
57
 
57
58
  if option
58
- pat = /\A(?:-[^\[\]=\s-](?:=|\s+)?|(--)?[^\[\]=\s-][^\[\]=\s]*(?(1)(?:=|\s+)|=))(["']).+\2\z/m
59
+ pat = /\A(?:-[^=\s-](?:=|\s+)?|(--)?[^=\s-][^=\s]*(?(1)(?:=|\s+)|=))(["']).+\2\z/m
59
60
  return val if val.match?(pat)
60
61
  end
61
62
  q = ->(s) { s.gsub("'\\\\''", "'") }
@@ -103,7 +104,7 @@ module Squared
103
104
  end
104
105
 
105
106
  def shell_split(val, join: nil, **kwargs)
106
- ret = val.shellsplit.map! { |opt| shell_escape(opt, double: true, option: true, **kwargs) }
107
+ ret = val.shellsplit.map! { |opt| shell_escape(opt, option: true, double: true, **kwargs) }
107
108
  return ret unless join
108
109
 
109
110
  ret.join(join.is_a?(::String) ? join : ' ')
@@ -12,15 +12,14 @@ module Squared
12
12
  include Rake::DSL
13
13
 
14
14
  class << self
15
- def parse(gem, namespace, ext = [pkg])
15
+ def parse(gem, namespace, ext = [gem])
16
16
  require gem
17
- obj = eval namespace
18
- Array(ext).each { |val| @@mime_obj[val] = [obj, ext] }
17
+ [eval(namespace), Array(ext)].tap do |data|
18
+ data.last.each { |key| @@mime_obj[key] = data }
19
+ end
19
20
  rescue LoadError, NameError => e
20
21
  warn e
21
22
  nil
22
- else
23
- @@mime_obj[ext.first]
24
23
  end
25
24
 
26
25
  def link(project, main = project.dependfile.basename, name = nil, **kwargs, &blk)
@@ -245,7 +244,7 @@ module Squared
245
244
  opt_style(theme[:key], /\A((?~ : ))( : (?!undefined).+)\z/m),
246
245
  opt_style(theme[:number], /\A((?~: ): )(-?[\d.]+)(\s*)\z/m, 2),
247
246
  opt_style(theme[:string], /\A((?~: ): ")(.+)("\s*)\z/m, 2),
248
- opt_style(theme[:hash], /\A((?~: ): \{)(.+)(\}\s*)\z/m, 2),
247
+ opt_style(theme[:hash], /\A((?~: ): \{)(.+)(}\s*)\z/m, 2),
249
248
  opt_style(theme[:array], /\A((?~: ): \[)(.+)(\]\s*)\z/m, 2),
250
249
  opt_style(theme[:boolean], /\A((?~: ): )(true|false)(\s*)\z/m, 2),
251
250
  opt_style(theme[:value], /\A((?~: ): (?!undefined))([^"\[{].*)\z/m, 2)
@@ -259,7 +258,7 @@ module Squared
259
258
  symbolize = opts[:symbolize_names]
260
259
  keys.each do |key|
261
260
  begin
262
- items = key.split('.')
261
+ items = key.split('.').flat_map { |name| name =~ /^(.+)\[(\d+)\]$/ ? [$1, $2.to_i] : name }
263
262
  items = items.map(&:to_sym) if symbolize
264
263
  val = data.dig(*items)
265
264
  if val.nil?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.6.2'
4
+ VERSION = '0.6.4'
5
5
  end