json-mask 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf429554b940fea4a030589830ad3eb3ad509983d365f19c0c371c5ff026b530
4
- data.tar.gz: 8ff525f2b92a3c4f8634c4c28b96f2dc54d2652a0b5a760f267cf3b7f9885627
3
+ metadata.gz: 9f93379e92b79b7ec9a8395dcc291f069f019fc3224557afef20c2d9727cea7b
4
+ data.tar.gz: 753a718f86cabc8e38314c41c865de587299bf83b7d9eaae6d1d0710607d25a4
5
5
  SHA512:
6
- metadata.gz: fb6a7be61059829fb6219b874fcfa41369070aecbd4792889e70a63a6d3c9aeb0b45b727a2cd80c6a2ee4297d695790d72b8c61fc668a1b2c73db64388805537
7
- data.tar.gz: 52f4c5e63190f7fe5cc238eb13b65b86e93696470d5a0b34704b14e7c46f814b76cf37fb5eea90c02d2d5befa600beaa209f7405f44d4c1289f2f04be8f39d41
6
+ metadata.gz: 9506600ebe233ba5b26cb97d2e76c887e6110429bcad82330a8ad531cfdf4dd0011d33fc159bf4ce23867591d9122e221d942d4564c82c4e9d638b75bb22b4d9
7
+ data.tar.gz: 3ea692d7b1969ec754177713345fd81cc5fd9b294f3697d118359b3564dd4d8f17bfc9b6f69efea4f6c696d414c77dd99a0de4c0cb689ed2432743d92d2be26e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 - 2026-09-09
4
+
5
+ - Add `CompiledMask#each_path` to enumerate selected paths with decoded field names and a distinct
6
+ `JsonMask::WILDCARD` token. Whole-field selections absorb descendants, and terminal wildcards
7
+ absorb sibling selections.
8
+ - Add `JsonMask.format_path` to turn a path or prefix into an escaped slash-separated selector.
9
+
3
10
  ## 0.2.0 - 2026-09-04
4
11
 
5
12
  - Expose compiled selectors for inspection. `CompiledMask#selection_tree` returns the root
data/README.md CHANGED
@@ -121,6 +121,48 @@ produce one by inspecting the compiled selector.
121
121
 
122
122
  ## Inspecting a compiled selector
123
123
 
124
+ ### Selected paths
125
+
126
+ `CompiledMask#each_path` yields a fresh array of segments for each selected path. Without a block,
127
+ it returns an `Enumerator`:
128
+
129
+ ```ruby
130
+ mask = JsonMask.compile("id,assets(url,width),*/name")
131
+
132
+ mask.each_path.to_a
133
+ # => [["id"], ["assets", "url"], ["assets", "width"], [JsonMask::WILDCARD, "name"]]
134
+
135
+ mask.each_path { |path| puts JsonMask.format_path(path) }
136
+ # id
137
+ # assets/url
138
+ # assets/width
139
+ # */name
140
+ ```
141
+
142
+ Named segments are unescaped strings. `JsonMask::WILDCARD` (the symbol `:*`) represents a wildcard;
143
+ the string `"*"` represents a literal field named `*`.
144
+
145
+ Paths follow the compiled tree: named fields are visited depth first, then the wildcard at each
146
+ level. Repeated named selections are merged. Selecting a whole field absorbs its descendants,
147
+ and a terminal wildcard absorbs its siblings: `id,id/extra` yields only `["id"]`, while `*,typo`
148
+ yields only `[JsonMask::WILDCARD]`. Nested wildcard paths stay separate from named paths.
149
+ Blank selectors yield no paths. With a block, `each_path` returns the compiled mask.
150
+
151
+ `JsonMask.format_path` converts a yielded path, or a prefix of one, into an escaped slash-separated
152
+ selector. It escapes structural characters, whitespace, and NUL so literal names survive parsing:
153
+
154
+ ```ruby
155
+ JsonMask.format_path(["a/b", "name"]) # => "a\\/b/name"
156
+ JsonMask.format_path(["*", "id"]) # => "\\*/id"
157
+ JsonMask.format_path([JsonMask::WILDCARD, "id"]) # => "*/id"
158
+ JsonMask.format_path([]) # => ""
159
+ ```
160
+
161
+ This lets an application inspect or validate paths without traversing the selection tree or
162
+ handling selector escaping itself.
163
+
164
+ ### Selection tree
165
+
124
166
  `JsonMask::CompiledMask#selection_tree` exposes the parsed selector as an immutable tree, for
125
167
  callers that need to examine a selector rather than apply it:
126
168
 
@@ -20,5 +20,34 @@ module JsonMask
20
20
  end
21
21
 
22
22
  alias filter call
23
+
24
+ # Yields selected paths with unescaped names and JsonMask::WILDCARD segments.
25
+ # Whole-field selections absorb descendants; a terminal wildcard absorbs siblings.
26
+ #
27
+ # @yieldparam path [Array<String, Symbol>] a fresh array for each selected path
28
+ # @return [Enumerator, CompiledMask] an enumerator without a block; self with a block
29
+ def each_path(&block)
30
+ return enum_for(__method__) unless block
31
+
32
+ walk_paths(selection_tree, [], &block) if selection_tree
33
+ self
34
+ end
35
+
36
+ private
37
+
38
+ def walk_paths(tree, prefix, &block)
39
+ return yield(prefix + [WILDCARD]) if tree.wildcard&.leaf?
40
+
41
+ selections = tree.named.to_a
42
+ selections << [WILDCARD, tree.wildcard] if tree.wildcard
43
+ selections.each do |name, selection|
44
+ path = prefix + [name]
45
+ if selection.leaf?
46
+ yield path
47
+ else
48
+ walk_paths(selection.children, path, &block)
49
+ end
50
+ end
51
+ end
23
52
  end
24
53
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JsonMask
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/json_mask.rb CHANGED
@@ -8,6 +8,8 @@ require_relative 'json_mask/projector'
8
8
  require_relative 'json_mask/compiled_mask'
9
9
 
10
10
  module JsonMask
11
+ WILDCARD = :*
12
+
11
13
  class << self
12
14
  def call(value, fields, **options)
13
15
  compile(fields, **options).call(value)
@@ -15,6 +17,16 @@ module JsonMask
15
17
 
16
18
  alias mask call
17
19
 
20
+ # Formats a path from CompiledMask#each_path, or a prefix of one, as a selector.
21
+ #
22
+ # @param path [Array<String, Symbol>] unescaped field names and WILDCARD segments
23
+ # @return [String] a slash-separated selector with literal names escaped
24
+ def format_path(path)
25
+ path.map do |name|
26
+ name == WILDCARD ? '*' : name.gsub(%r{[\\,/()*\s\x00]}) { |character| "\\#{character}" }
27
+ end.join('/')
28
+ end
29
+
18
30
  def compile(
19
31
  fields,
20
32
  max_length: Parser::DEFAULT_MAX_LENGTH,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-mask
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Sheldon