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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +42 -0
- data/lib/json_mask/compiled_mask.rb +29 -0
- data/lib/json_mask/version.rb +1 -1
- data/lib/json_mask.rb +12 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9f93379e92b79b7ec9a8395dcc291f069f019fc3224557afef20c2d9727cea7b
|
|
4
|
+
data.tar.gz: 753a718f86cabc8e38314c41c865de587299bf83b7d9eaae6d1d0710607d25a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
data/lib/json_mask/version.rb
CHANGED
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,
|