json_p3 0.4.1 → 1.0.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
- checksums.yaml.gz.sig +0 -0
- data/.rubocop.yml +26 -9
- data/.ruby-version +1 -1
- data/CHANGELOG.md +54 -0
- data/README.md +125 -123
- data/Rakefile +3 -3
- data/certs/jgrp.pem +21 -21
- data/lib/json_p3/errors.rb +51 -43
- data/lib/json_p3/patch/op.rb +23 -0
- data/lib/json_p3/patch/op_add.rb +51 -0
- data/lib/json_p3/patch/op_copy.rb +64 -0
- data/lib/json_p3/patch/op_move.rb +74 -0
- data/lib/json_p3/patch/op_remove.rb +56 -0
- data/lib/json_p3/patch/op_replace.rb +54 -0
- data/lib/json_p3/patch/op_test.rb +31 -0
- data/lib/json_p3/patch.rb +15 -330
- data/lib/json_p3/path/environment.rb +113 -0
- data/lib/json_p3/path/filter.rb +463 -0
- data/lib/json_p3/path/function.rb +12 -0
- data/lib/json_p3/path/function_extensions/count.rb +15 -0
- data/lib/json_p3/path/function_extensions/length.rb +17 -0
- data/lib/json_p3/path/function_extensions/match.rb +62 -0
- data/lib/json_p3/path/function_extensions/pattern.rb +42 -0
- data/lib/json_p3/path/function_extensions/search.rb +44 -0
- data/lib/json_p3/path/function_extensions/value.rb +15 -0
- data/lib/json_p3/path/lexer.rb +220 -0
- data/lib/json_p3/path/node.rb +48 -0
- data/lib/json_p3/path/parser.rb +676 -0
- data/lib/json_p3/path/query.rb +74 -0
- data/lib/json_p3/path/segment.rb +172 -0
- data/lib/json_p3/path/selector.rb +304 -0
- data/lib/json_p3/path/serialize.rb +16 -0
- data/lib/json_p3/path/unescape.rb +134 -0
- data/lib/json_p3/pointer.rb +15 -76
- data/lib/json_p3/relative_pointer.rb +69 -0
- data/lib/json_p3/version.rb +1 -1
- data/lib/json_p3.rb +50 -13
- data/sig/json_p3/cache.rbs +21 -0
- data/sig/json_p3/errors.rbs +55 -0
- data/sig/json_p3/patch.rbs +145 -0
- data/sig/json_p3/path/environment.rbs +81 -0
- data/sig/json_p3/path/filter.rbs +196 -0
- data/sig/json_p3/path/function.rbs +94 -0
- data/sig/json_p3/path/lexer.rbs +62 -0
- data/sig/json_p3/path/node.rbs +46 -0
- data/sig/json_p3/path/parser.rbs +92 -0
- data/sig/json_p3/path/query.rbs +47 -0
- data/sig/json_p3/path/segment.rbs +54 -0
- data/sig/json_p3/path/selector.rbs +100 -0
- data/sig/json_p3/path/serialize.rbs +9 -0
- data/sig/json_p3/path/unescape.rbs +12 -0
- data/sig/json_p3/pointer.rbs +64 -0
- data/sig/json_p3/relative_pointer.rbs +30 -0
- data/sig/json_p3.rbs +24 -1318
- data.tar.gz.sig +0 -0
- metadata +66 -46
- metadata.gz.sig +0 -0
- data/lib/json_p3/environment.rb +0 -111
- data/lib/json_p3/filter.rb +0 -459
- data/lib/json_p3/function.rb +0 -10
- data/lib/json_p3/function_extensions/count.rb +0 -15
- data/lib/json_p3/function_extensions/length.rb +0 -17
- data/lib/json_p3/function_extensions/match.rb +0 -62
- data/lib/json_p3/function_extensions/pattern.rb +0 -39
- data/lib/json_p3/function_extensions/search.rb +0 -44
- data/lib/json_p3/function_extensions/value.rb +0 -15
- data/lib/json_p3/lexer.rb +0 -440
- data/lib/json_p3/node.rb +0 -44
- data/lib/json_p3/parser.rb +0 -553
- data/lib/json_p3/path.rb +0 -72
- data/lib/json_p3/segment.rb +0 -158
- data/lib/json_p3/selector.rb +0 -306
- data/lib/json_p3/serialize.rb +0 -13
- data/lib/json_p3/token.rb +0 -36
- data/lib/json_p3/unescape.rb +0 -112
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c8afa8205821093144f9c2ccbe427e7d726251e48e8cc1480152d0efc8ade7c
|
|
4
|
+
data.tar.gz: 77639923975a6f89d10cbad38891c59015f06797f084ccaacc78073a5e4161ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 178a01fe4d486b0d2787d46edbe93dce36a6fe6b2b30410daaa57f44da385649aad995c1187ac55253d8cdbe2448f86c73bdd56f1142ba066364c24cd9a2dfe9
|
|
7
|
+
data.tar.gz: 82d38fc46b9d1049135e79d1b3b2aae814ef65285153f5b57242021b7b9b5da5f55f2b5c092cd0e301b6f5ef0b047d59d354bb998c92fe522ecdb2804bc688e9
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/.rubocop.yml
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
plugins:
|
|
2
2
|
- rubocop-minitest
|
|
3
3
|
- rubocop-rake
|
|
4
|
-
|
|
5
|
-
plugins:
|
|
6
4
|
- rubocop-performance
|
|
7
5
|
|
|
8
6
|
AllCops:
|
|
9
|
-
TargetRubyVersion: 3.
|
|
7
|
+
TargetRubyVersion: 3.3
|
|
10
8
|
NewCops: enable
|
|
11
9
|
|
|
12
10
|
Style/StringLiterals:
|
|
@@ -15,21 +13,40 @@ Style/StringLiterals:
|
|
|
15
13
|
Style/StringLiteralsInInterpolation:
|
|
16
14
|
EnforcedStyle: double_quotes
|
|
17
15
|
|
|
16
|
+
Style/ComparableBetween:
|
|
17
|
+
Enabled: false
|
|
18
|
+
|
|
19
|
+
Layout/LeadingCommentSpace:
|
|
20
|
+
Enabled: false
|
|
21
|
+
|
|
18
22
|
Metrics/AbcSize:
|
|
19
|
-
|
|
23
|
+
Enabled: false
|
|
20
24
|
|
|
21
25
|
Metrics/ClassLength:
|
|
22
|
-
|
|
26
|
+
Enabled: false
|
|
23
27
|
|
|
24
28
|
Metrics/CyclomaticComplexity:
|
|
25
|
-
|
|
29
|
+
Enabled: false
|
|
26
30
|
|
|
27
31
|
Metrics/MethodLength:
|
|
28
|
-
|
|
32
|
+
Enabled: false
|
|
29
33
|
|
|
30
34
|
Metrics/PerceivedComplexity:
|
|
31
|
-
|
|
35
|
+
Enabled: false
|
|
36
|
+
|
|
37
|
+
Metrics/BlockNesting:
|
|
38
|
+
Max: 4
|
|
39
|
+
|
|
40
|
+
Metrics/BlockLength:
|
|
41
|
+
Enabled: false
|
|
42
|
+
|
|
43
|
+
Metrics/ModuleLength:
|
|
44
|
+
Enabled: false
|
|
32
45
|
|
|
33
46
|
Naming/MethodParameterName:
|
|
34
47
|
AllowedNames:
|
|
35
48
|
- op
|
|
49
|
+
- ch
|
|
50
|
+
|
|
51
|
+
Naming/PredicateMethod:
|
|
52
|
+
Enabled: false
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
## [1.0.0] - 2026-04-21
|
|
2
|
+
|
|
3
|
+
This major release includes breaking changes. No new features have been added.
|
|
4
|
+
|
|
5
|
+
- Changed the JSONPath tokenizer and parser. The new parser is faster and more JIT friendly.
|
|
6
|
+
- Changed our module and class layout to better separate JSONPath, JSON Pointer and JSON Patch, and to remove "stuttering". For example, previously we had `JSONP3::JSONPathEnvironment` and `JSONP3::JSONPathNode`. Now we have `JSONP3::Path::Environment` and `JSONP3::Path:Node`. This ASCII tree view show our new module and class hierarchy.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
module JSONP3
|
|
10
|
+
├── class Error < StandardError
|
|
11
|
+
│
|
|
12
|
+
├── module Path # JSONPath (RFC 9535)
|
|
13
|
+
│ ├── class Environment
|
|
14
|
+
│ ├── class Query
|
|
15
|
+
│ ├── class Node
|
|
16
|
+
│ ├── class NodeList
|
|
17
|
+
│ ├── class Error < JSONP3::Error
|
|
18
|
+
│ │ ├── class SyntaxError
|
|
19
|
+
│ │ ├── class TypeError
|
|
20
|
+
│ │ ├── class NameError
|
|
21
|
+
│ │ └── class RecursionError
|
|
22
|
+
│ ├── def self.find
|
|
23
|
+
│ ├── def self.find_enum
|
|
24
|
+
│ ├── def self.compile
|
|
25
|
+
│ ├── def self.match
|
|
26
|
+
│ ├── def self.match?
|
|
27
|
+
│ └── def self.first
|
|
28
|
+
│
|
|
29
|
+
├── class Pointer # JSON Pointer (RFC 6901)
|
|
30
|
+
│ ├── class Error < JSONP3::Error
|
|
31
|
+
│ │ ├── class IndexError
|
|
32
|
+
│ │ ├── class SyntaxError
|
|
33
|
+
│ │ └── class TypeError
|
|
34
|
+
│ └── def self.resolve
|
|
35
|
+
│
|
|
36
|
+
└── class Patch # JSON Patch (RFC 6902)
|
|
37
|
+
├── class OpAdd
|
|
38
|
+
├── class OpCopy
|
|
39
|
+
├── class OpMove
|
|
40
|
+
├── class OpRemove
|
|
41
|
+
├── class OpReplace
|
|
42
|
+
├── class OpTest
|
|
43
|
+
├── class Error < JSONP3::Error
|
|
44
|
+
│ └── class TestFailure
|
|
45
|
+
└── def !self.apply
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Note that top-level convenience methods - like `JSONP3.find` and `JSONP3.compile` - are unchanged. Most dependent projects should only need to change error class names.
|
|
49
|
+
|
|
50
|
+
- Renamed `JSONP3::JSONPath` to `JSONP3::Path::Query`.
|
|
51
|
+
- Renamed `JSONP3::RecursiveDescentSegment` to `JSONP3::Path::DescendantSegment` to better match the spec.
|
|
52
|
+
- Renamed `JSONP3.apply` to `JSONP3.apply!`, `JSONP3::Patch#apply` to `JSONP3::Patch#apply!` and `JSONP3::Patch::Op#apply` to `JSONP3::Patch::Op#apply!`. There is no "safe", non mutating version of `apply!`.
|
|
53
|
+
- Dropped support for Ruby 3.1 and 3.2, they are end of life.
|
|
54
|
+
|
|
1
55
|
## [0.4.1] - 2025-03-18
|
|
2
56
|
|
|
3
57
|
- Fixed `JSONPathSyntaxError` claiming "unbalanced parentheses" when the query has balanced brackets.
|
data/README.md
CHANGED
|
@@ -16,7 +16,7 @@ We follow <a href="https://datatracker.ietf.org/doc/html/rfc9535">RFC 9535</a> s
|
|
|
16
16
|
<img alt="Gem Version" src="https://img.shields.io/gem/v/json_p3?style=flat-square">
|
|
17
17
|
</a>
|
|
18
18
|
<a href="https://github.com/jg-rp/ruby-json-p3">
|
|
19
|
-
<img alt="Static Badge" src="https://img.shields.io/badge/Ruby-3.
|
|
19
|
+
<img alt="Static Badge" src="https://img.shields.io/badge/Ruby-3.3%20%7C%203.4%20%7C%204.0-CC342D?style=flat-square">
|
|
20
20
|
</a>
|
|
21
21
|
</p>
|
|
22
22
|
|
|
@@ -29,6 +29,7 @@ We follow <a href="https://datatracker.ietf.org/doc/html/rfc9535">RFC 9535</a> s
|
|
|
29
29
|
- [Links](#links)
|
|
30
30
|
- [Related projects](#related-projects)
|
|
31
31
|
- [Quick start](#quick-start)
|
|
32
|
+
- [Class and module layout](#module-layout)
|
|
32
33
|
- [Contributing](#contributing)
|
|
33
34
|
|
|
34
35
|
## Install
|
|
@@ -36,7 +37,7 @@ We follow <a href="https://datatracker.ietf.org/doc/html/rfc9535">RFC 9535</a> s
|
|
|
36
37
|
Add `'json_p3'` to your Gemfile:
|
|
37
38
|
|
|
38
39
|
```
|
|
39
|
-
gem 'json_p3', '~> 0.
|
|
40
|
+
gem 'json_p3', '~> 1.0.0'
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
Or
|
|
@@ -45,6 +46,12 @@ Or
|
|
|
45
46
|
gem install json_p3
|
|
46
47
|
```
|
|
47
48
|
|
|
49
|
+
Or
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
bundle add json_p3
|
|
53
|
+
```
|
|
54
|
+
|
|
48
55
|
### Checksum
|
|
49
56
|
|
|
50
57
|
JSON P3 is cryptographically signed. To be sure the gem you install hasn't been tampered with, add my public key (if you haven't already) as a trusted certificate:
|
|
@@ -70,24 +77,10 @@ require "json"
|
|
|
70
77
|
data = JSON.parse <<~JSON
|
|
71
78
|
{
|
|
72
79
|
"users": [
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
{
|
|
78
|
-
"name": "Sally",
|
|
79
|
-
"score": 84,
|
|
80
|
-
"admin": false
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
"name": "John",
|
|
84
|
-
"score": 86,
|
|
85
|
-
"admin": true
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
"name": "Jane",
|
|
89
|
-
"score": 55
|
|
90
|
-
}
|
|
80
|
+
{ "name": "Sue", "score": 100 },
|
|
81
|
+
{ "name": "Sally", "score": 84, "admin": false },
|
|
82
|
+
{ "name": "John", "score": 86, "admin": true },
|
|
83
|
+
{ "name": "Jane", "score": 55 }
|
|
91
84
|
],
|
|
92
85
|
"moderator": "John"
|
|
93
86
|
}
|
|
@@ -144,15 +137,15 @@ end
|
|
|
144
137
|
|
|
145
138
|
### find
|
|
146
139
|
|
|
147
|
-
`find(query, value) -> Array
|
|
140
|
+
`find: (String query, top value) -> Array[JSONP3::Path::Node]`
|
|
148
141
|
|
|
149
|
-
Apply
|
|
142
|
+
Apply query expression _query_ to JSON-like data _value_. An array of `Node` instances is returned, one node for each value matched by _query_.
|
|
150
143
|
|
|
151
|
-
|
|
144
|
+
For each `Node`:
|
|
152
145
|
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
146
|
+
- `#value` is the JSON-like value associated with the node.
|
|
147
|
+
- `#location` is an array of object keys and array indices used to reach the node's value in the target document.
|
|
148
|
+
- `#path` returns the normalized path to the node in the target JSON document. Normalized paths are computed from `#location` and are not cached between calls.
|
|
156
149
|
|
|
157
150
|
```ruby
|
|
158
151
|
require "json_p3"
|
|
@@ -161,24 +154,10 @@ require "json"
|
|
|
161
154
|
data = JSON.parse <<~JSON
|
|
162
155
|
{
|
|
163
156
|
"users": [
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
{
|
|
169
|
-
"name": "Sally",
|
|
170
|
-
"score": 84,
|
|
171
|
-
"admin": false
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
"name": "John",
|
|
175
|
-
"score": 86,
|
|
176
|
-
"admin": true
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
"name": "Jane",
|
|
180
|
-
"score": 55
|
|
181
|
-
}
|
|
157
|
+
{ "name": "Sue", "score": 100 },
|
|
158
|
+
{ "name": "Sally", "score": 84, "admin": false },
|
|
159
|
+
{ "name": "John", "score": 86, "admin": true },
|
|
160
|
+
{ "name": "Jane", "score": 55 }
|
|
182
161
|
],
|
|
183
162
|
"moderator": "John"
|
|
184
163
|
}
|
|
@@ -192,11 +171,13 @@ end
|
|
|
192
171
|
# {"name"=>"John", "score"=>86, "admin"=>true} at $['users'][2]
|
|
193
172
|
```
|
|
194
173
|
|
|
174
|
+
The returned array will be empty if there were no matches.
|
|
175
|
+
|
|
195
176
|
### find_enum
|
|
196
177
|
|
|
197
|
-
`find_enum(query, value) -> Enumerable
|
|
178
|
+
`find_enum: (String query, top value) -> Enumerable[JSONP3::Path::Node]`
|
|
198
179
|
|
|
199
|
-
`find_enum` is an alternative to `find` which returns an enumerable (usually an enumerator) of `
|
|
180
|
+
`JSONP3.find_enum` is an alternative to `find` which returns an enumerable (usually an enumerator) of `Node` instances instead of an array. Depending on the query and the data the query is applied to, `find_enum` can be more efficient than `find`, especially for large data and queries using descendant segments.
|
|
200
181
|
|
|
201
182
|
```ruby
|
|
202
183
|
# ... continued from above
|
|
@@ -211,9 +192,9 @@ end
|
|
|
211
192
|
|
|
212
193
|
### compile
|
|
213
194
|
|
|
214
|
-
`compile(query) ->
|
|
195
|
+
`compile: (String query) -> JSONP3::Path::Query`
|
|
215
196
|
|
|
216
|
-
Prepare a
|
|
197
|
+
Prepare a query for repeated application to different JSON-like data. An instance of `Query` has `#find(data)`, which behaves similarly to the module-level `find(query, data)` method.
|
|
217
198
|
|
|
218
199
|
```ruby
|
|
219
200
|
require "json_p3"
|
|
@@ -222,24 +203,10 @@ require "json"
|
|
|
222
203
|
data = JSON.parse <<~JSON
|
|
223
204
|
{
|
|
224
205
|
"users": [
|
|
225
|
-
{
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
{
|
|
230
|
-
"name": "Sally",
|
|
231
|
-
"score": 84,
|
|
232
|
-
"admin": false
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
"name": "John",
|
|
236
|
-
"score": 86,
|
|
237
|
-
"admin": true
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
"name": "Jane",
|
|
241
|
-
"score": 55
|
|
242
|
-
}
|
|
206
|
+
{ "name": "Sue", "score": 100 },
|
|
207
|
+
{ "name": "Sally", "score": 84, "admin": false },
|
|
208
|
+
{ "name": "John", "score": 86, "admin": true },
|
|
209
|
+
{ "name": "Jane", "score": 55 }
|
|
243
210
|
],
|
|
244
211
|
"moderator": "John"
|
|
245
212
|
}
|
|
@@ -257,48 +224,48 @@ end
|
|
|
257
224
|
|
|
258
225
|
### match / first
|
|
259
226
|
|
|
260
|
-
`match(query, value) ->
|
|
227
|
+
`match: (String query, top value) -> (JSONP3::Path::Node | nil)`
|
|
261
228
|
|
|
262
|
-
`match` (alias `first`) returns a node for the first available match when applying _query_ to _value_, or `nil` if there were no matches.
|
|
229
|
+
`JSONP3.match` (alias `first`) returns a node for the first available match when applying _query_ to _value_, or `nil` if there were no matches.
|
|
263
230
|
|
|
264
231
|
### match?
|
|
265
232
|
|
|
266
|
-
`match
|
|
233
|
+
`match?: (String query, top value) -> bool`
|
|
267
234
|
|
|
268
|
-
`match?` returns `true` if there was at least one match from applying _query_ to _value_, or `false` otherwise.
|
|
235
|
+
`JSONP3.match?` returns `true` if there was at least one match from applying _query_ to _value_, or `false` otherwise.
|
|
269
236
|
|
|
270
|
-
###
|
|
237
|
+
### JSONP3::Path::Environment
|
|
271
238
|
|
|
272
239
|
The `find`, `find_enum` and `compile` methods described above are convenience methods equivalent to:
|
|
273
240
|
|
|
274
241
|
```
|
|
275
|
-
JSONP3::DEFAULT_ENVIRONMENT.find(query, data)
|
|
242
|
+
JSONP3::Path::DEFAULT_ENVIRONMENT.find(query, data)
|
|
276
243
|
```
|
|
277
244
|
|
|
278
245
|
```
|
|
279
|
-
JSONP3::DEFAULT_ENVIRONMENT.find_enum(query, data)
|
|
246
|
+
JSONP3::Path::DEFAULT_ENVIRONMENT.find_enum(query, data)
|
|
280
247
|
```
|
|
281
248
|
|
|
282
249
|
and
|
|
283
250
|
|
|
284
251
|
```
|
|
285
|
-
JSONP3::DEFAULT_ENVIRONMENT.compile(query)
|
|
252
|
+
JSONP3::Path::DEFAULT_ENVIRONMENT.compile(query)
|
|
286
253
|
```
|
|
287
254
|
|
|
288
|
-
You
|
|
255
|
+
You can create your own environment like this:
|
|
289
256
|
|
|
290
257
|
```ruby
|
|
291
258
|
require "json_p3"
|
|
292
259
|
|
|
293
|
-
jsonpath = JSONP3::
|
|
260
|
+
jsonpath = JSONP3::Path::Environment.new
|
|
294
261
|
nodes = jsonpath.find("$.*", { "a" => "b", "c" => "d" })
|
|
295
262
|
pp nodes.map(&:value) # ["b", "d"]
|
|
296
263
|
```
|
|
297
264
|
|
|
298
|
-
To configure an environment with custom filter functions or non-standard selectors, inherit from `
|
|
265
|
+
To configure an environment with custom filter functions or non-standard selectors, inherit from `JSONP3::Path::Environment` and override some of its constants or the `#setup_function_extensions` method.
|
|
299
266
|
|
|
300
267
|
```ruby
|
|
301
|
-
class MyJSONPathEnvironment < JSONP3::
|
|
268
|
+
class MyJSONPathEnvironment < JSONP3::Path::Environment
|
|
302
269
|
# The maximum integer allowed when selecting array items by index.
|
|
303
270
|
MAX_INT_INDEX = (2**53) - 1
|
|
304
271
|
|
|
@@ -326,26 +293,33 @@ class MyJSONPathEnvironment < JSONP3::JSONPathEnvironment
|
|
|
326
293
|
# Override this function to configure JSONPath function extensions.
|
|
327
294
|
# By default, only the standard functions described in RFC 9535 are enabled.
|
|
328
295
|
def setup_function_extensions
|
|
329
|
-
@function_extensions["length"] = Length.new
|
|
330
|
-
@function_extensions["count"] = Count.new
|
|
331
|
-
@function_extensions["value"] = Value.new
|
|
332
|
-
@function_extensions["match"] = Match.new
|
|
333
|
-
@function_extensions["search"] = Search.new
|
|
296
|
+
@function_extensions["length"] = JSONP3::Path::Length.new
|
|
297
|
+
@function_extensions["count"] = JSONP3::Path::Count.new
|
|
298
|
+
@function_extensions["value"] = JSONP3::Path::Value.new
|
|
299
|
+
@function_extensions["match"] = JSONP3::Path::Match.new
|
|
300
|
+
@function_extensions["search"] = JSONP3::Path::Search.new
|
|
334
301
|
end
|
|
335
302
|
```
|
|
336
303
|
|
|
337
|
-
###
|
|
304
|
+
### Errors
|
|
338
305
|
|
|
339
|
-
`
|
|
306
|
+
`JSONP3::Error` is the base class for all exceptions raised from this Gem.
|
|
340
307
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
308
|
+
```
|
|
309
|
+
StandardError
|
|
310
|
+
└── JSONP3::Error
|
|
311
|
+
├── JSONP3::Path::Error
|
|
312
|
+
│ └── SyntaxError, TypeError, NameError, RecursionError
|
|
313
|
+
├── JSONP3::Pointer::Error
|
|
314
|
+
│ └── IndexError, SyntaxError, TypeError
|
|
315
|
+
└── JSONP3::Patch::Error
|
|
316
|
+
└── TestFailure
|
|
317
|
+
```
|
|
344
318
|
|
|
345
|
-
`
|
|
319
|
+
`JSONP3::Path::Error` inherits from `JSONP3::Error` and implements `#detailed_message`. With recent versions of Ruby you should get useful error messages.
|
|
346
320
|
|
|
347
321
|
```
|
|
348
|
-
JSONP3::
|
|
322
|
+
JSONP3::Path::SyntaxError: unexpected trailing whitespace
|
|
349
323
|
-> '$.foo ' 1:5
|
|
350
324
|
|
|
|
351
325
|
1 | $.foo
|
|
@@ -354,9 +328,9 @@ JSONP3::JSONPathSyntaxError: unexpected trailing whitespace
|
|
|
354
328
|
|
|
355
329
|
### resolve
|
|
356
330
|
|
|
357
|
-
`resolve(pointer, value) -> Object`
|
|
331
|
+
`resolve: (String pointer, top value, ?default: top) -> Object`
|
|
358
332
|
|
|
359
|
-
Resolve a JSON Pointer (RFC 6901) against some data using `JSONP3.resolve
|
|
333
|
+
Resolve a JSON Pointer (RFC 6901) against some data using `JSONP3.resolve`.
|
|
360
334
|
|
|
361
335
|
```ruby
|
|
362
336
|
require "json_p3"
|
|
@@ -365,24 +339,10 @@ require "json"
|
|
|
365
339
|
data = JSON.parse <<~JSON
|
|
366
340
|
{
|
|
367
341
|
"users": [
|
|
368
|
-
{
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
372
|
-
{
|
|
373
|
-
"name": "Sally",
|
|
374
|
-
"score": 84,
|
|
375
|
-
"admin": false
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
"name": "John",
|
|
379
|
-
"score": 86,
|
|
380
|
-
"admin": true
|
|
381
|
-
},
|
|
382
|
-
{
|
|
383
|
-
"name": "Jane",
|
|
384
|
-
"score": 55
|
|
385
|
-
}
|
|
342
|
+
{ "name": "Sue", "score": 100 },
|
|
343
|
+
{ "name": "Sally", "score": 84, "admin": false },
|
|
344
|
+
{ "name": "John", "score": 86, "admin": true },
|
|
345
|
+
{ "name": "Jane", "score": 55 }
|
|
386
346
|
],
|
|
387
347
|
"moderator": "John"
|
|
388
348
|
}
|
|
@@ -392,7 +352,7 @@ puts JSONP3.resolve("/users/1", data)
|
|
|
392
352
|
# {"name"=>"Sally", "score"=>84, "admin"=>false}
|
|
393
353
|
```
|
|
394
354
|
|
|
395
|
-
If a pointer can not be resolved, `JSONP3::
|
|
355
|
+
If a pointer can not be resolved, `JSONP3::Pointer::UNDEFINED` is returned instead. You can use your own default value using the `default:` keyword argument.
|
|
396
356
|
|
|
397
357
|
```ruby
|
|
398
358
|
# continued from above
|
|
@@ -400,11 +360,11 @@ If a pointer can not be resolved, `JSONP3::JSONPointer::UNDEFINED` is returned i
|
|
|
400
360
|
pp JSONP3.resolve("/no/such/thing", data, default: nil) # nil
|
|
401
361
|
```
|
|
402
362
|
|
|
403
|
-
### apply
|
|
363
|
+
### apply!
|
|
404
364
|
|
|
405
|
-
`apply(ops, value) -> Object`
|
|
365
|
+
`apply!: (Array[Patch::Op | Hash[String, untyped]] ops, top value) -> Object`
|
|
406
366
|
|
|
407
|
-
Apply a JSON Patch ([RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902)) with `JSONP3.apply
|
|
367
|
+
Apply a JSON Patch ([RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902)) with `JSONP3.apply!`. **Data is modified in place**.
|
|
408
368
|
|
|
409
369
|
```ruby
|
|
410
370
|
require "json"
|
|
@@ -420,31 +380,73 @@ ops = <<~JSON
|
|
|
420
380
|
JSON
|
|
421
381
|
|
|
422
382
|
data = { "some" => { "other" => "thing" } }
|
|
423
|
-
JSONP3.apply(JSON.parse(ops), data)
|
|
383
|
+
JSONP3.apply!(JSON.parse(ops), data)
|
|
424
384
|
pp data
|
|
425
385
|
# {"some"=>{"other"=>"thing", "foo"=>{"bar"=>[1], "else"=>"thing"}}}
|
|
426
386
|
```
|
|
427
387
|
|
|
428
|
-
`JSONP3.apply(ops, value)` is a convenience method equivalent to `JSONP3::
|
|
388
|
+
`JSONP3.apply!(ops, value)` is a convenience method equivalent to `JSONP3::Patch.new(ops).apply!(value)`. Use the `Patch` constructor when you need to apply the same patch to different data.
|
|
429
389
|
|
|
430
|
-
As well as passing an array of hashes following RFC 6902 as ops to `
|
|
390
|
+
As well as passing an array of hashes following RFC 6902 as ops to `Patch`, we offer a builder API to construct JSON Patch documents programmatically.
|
|
431
391
|
|
|
432
392
|
```ruby
|
|
433
393
|
require "json_p3"
|
|
434
394
|
|
|
435
395
|
data = { "some" => { "other" => "thing" } }
|
|
436
396
|
|
|
437
|
-
patch = JSONP3::
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
397
|
+
patch = JSONP3::Patch.new
|
|
398
|
+
.add("/some/foo", { "foo" => [] })
|
|
399
|
+
.add("/some/foo", { "bar" => [] })
|
|
400
|
+
.copy("/some/other", "/some/foo/else")
|
|
401
|
+
.copy("/some/foo/else", "/some/foo/bar/-")
|
|
442
402
|
|
|
443
|
-
patch.apply(data)
|
|
403
|
+
patch.apply!(data)
|
|
444
404
|
pp data
|
|
445
405
|
# {"some"=>{"other"=>"thing", "foo"=>{"bar"=>["thing"], "else"=>"thing"}}}
|
|
446
406
|
```
|
|
447
407
|
|
|
408
|
+
## Module layout
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
module JSONP3
|
|
412
|
+
├── class Error < StandardError
|
|
413
|
+
│
|
|
414
|
+
├── module Path # JSONPath (RFC 9535)
|
|
415
|
+
│ ├── class Environment
|
|
416
|
+
│ ├── class Query
|
|
417
|
+
│ ├── class Node
|
|
418
|
+
│ ├── class NodeList
|
|
419
|
+
│ ├── class Error < JSONP3::Error
|
|
420
|
+
│ │ ├── class SyntaxError
|
|
421
|
+
│ │ ├── class TypeError
|
|
422
|
+
│ │ ├── class NameError
|
|
423
|
+
│ │ └── class RecursionError
|
|
424
|
+
│ ├── def self.find
|
|
425
|
+
│ ├── def self.find_enum
|
|
426
|
+
│ ├── def self.compile
|
|
427
|
+
│ ├── def self.match
|
|
428
|
+
│ ├── def self.match?
|
|
429
|
+
│ └── def self.first
|
|
430
|
+
│
|
|
431
|
+
├── class Pointer # JSON Pointer (RFC 6901)
|
|
432
|
+
│ ├── class Error < JSONP3::Error
|
|
433
|
+
│ │ ├── class IndexError
|
|
434
|
+
│ │ ├── class SyntaxError
|
|
435
|
+
│ │ └── class TypeError
|
|
436
|
+
│ └── def self.resolve
|
|
437
|
+
│
|
|
438
|
+
└── class Patch # JSON Patch (RFC 6902)
|
|
439
|
+
├── class OpAdd
|
|
440
|
+
├── class OpCopy
|
|
441
|
+
├── class OpMove
|
|
442
|
+
├── class OpRemove
|
|
443
|
+
├── class OpReplace
|
|
444
|
+
├── class OpTest
|
|
445
|
+
├── class Error < JSONP3::Error
|
|
446
|
+
│ └── class TestFailure
|
|
447
|
+
└── def self.!apply
|
|
448
|
+
```
|
|
449
|
+
|
|
448
450
|
## Contributing
|
|
449
451
|
|
|
450
452
|
Your contributions and questions are always welcome. Feel free to ask questions, report bugs or request features on the [issue tracker](https://github.com/jg-rp/ruby-json-p3/issues) or on [Github Discussions](https://github.com/jg-rp/ruby-json-p3/discussions). Pull requests are welcome too.
|
data/Rakefile
CHANGED
|
@@ -8,9 +8,9 @@ Minitest::TestTask.create
|
|
|
8
8
|
require "rubocop/rake_task"
|
|
9
9
|
|
|
10
10
|
RuboCop::RakeTask.new do |task|
|
|
11
|
-
task.
|
|
12
|
-
task.
|
|
13
|
-
task.
|
|
11
|
+
task.plugins << "rubocop-minitest"
|
|
12
|
+
task.plugins << "rubocop-rake"
|
|
13
|
+
task.plugins << "rubocop-performance"
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
require "steep/rake_task"
|
data/certs/jgrp.pem
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
-----BEGIN CERTIFICATE-----
|
|
2
2
|
MIIEhTCCAu2gAwIBAgIBATANBgkqhkiG9w0BAQsFADBEMRYwFAYDVQQDDA1qYW1l
|
|
3
3
|
c2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
|
|
4
|
-
|
|
4
|
+
FgNjb20wHhcNMjUxMDI1MDc0ODI4WhcNMjYxMDI1MDc0ODI4WjBEMRYwFAYDVQQD
|
|
5
5
|
DA1qYW1lc2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
|
|
6
|
-
k/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
AaOBgTB/
|
|
16
|
-
|
|
6
|
+
k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDYTvQW
|
|
7
|
+
0JzsHenYu4w0iHgJRtU9uK8vkDx1wlMioKlPFkaiMfxqUcTTfg80U/n8n/KKmpdq
|
|
8
|
+
y2rpUqv+VPE2nrfCUncf7ukqs+gF8uehbciMXV4iOzsZmqmtlPUJiGuSyO8REe/O
|
|
9
|
+
ERAyxYV8y5psF+WnW4uQzsWCKTnKbexIc6rf237veuYvYoBpnodJu0mJ0m9tBCFU
|
|
10
|
+
qH3Nk1sHBRIEEwn+nTiIiOMb+QMU5p0NIQkIhJO3el5nEQgIGxMPnie7PnwxPquQ
|
|
11
|
+
1rouuISbLmQy5oKYc9kScwnHdAu2PtnnJFNNuQ37vED375iQgMem/tK8EgMm9/Qr
|
|
12
|
+
Qxd2DQqqehgtEySWntGHTxNLXQouqHIRyqzkbcCatJqC+PBBql1x2N1KqgyvbLM3
|
|
13
|
+
B0dZCdJECr5J8zrluMxYsy7uGMmLz3LZ7PskGaJg4XxXP3j/B/e03/AxDj0neZZu
|
|
14
|
+
xuDLIfMDSNILO1pskMc+Xp0xdnkbS4IjiMDTrhqABuEcdNYH/GpyTiA/I0cCAwEA
|
|
15
|
+
AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBR4j7UhnpT9
|
|
16
|
+
mLcAkshwkFtwVpMdejAiBgNVHREEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNv
|
|
17
17
|
bTAiBgNVHRIEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNvbTANBgkqhkiG9w0B
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
AQsFAAOCAYEAbTkg946BsGPQFX3jskBjwINeFnMP0IppuStonPQv3SNQu1SWP0xS
|
|
19
|
+
o1Qji1dxkWDfOcp1ffrRMUo7ecrJbTqQ5Sdx+7zw0OLodSVqSu1LMIVDw/T73smg
|
|
20
|
+
ADO2r3eZ8vxCKh7z4wUeKXvmBXi5NvpmvhH7D+qeG4OMLI8jnfmfbSHZQZbe2tpd
|
|
21
|
+
tJC+MYVok/m91fBrfv5wqLsR5QiJl3vcVhG+xACd46gr3JZFOxBqpSJrCYL8Gwqx
|
|
22
|
+
BaKEySCLGqkb0d1Rnt2/zTR2KtW2QJVH9rBMnJ+LsPEFKs1UHH4qHzxmwmmaQLon
|
|
23
|
+
MoE4UJ51GHGrjJ4WsHOUSOy5UbjaQec3kuIDwTXKbHws0J0Ut9S4S38opZKdQHDD
|
|
24
|
+
PUBpY0QA1CgE/VjKAYEDHOJyfUCNOl+sEESM+WxEXZAUVZLrFF7MljJqyQq+JyTD
|
|
25
|
+
vFKHe1ZAA76NFrPscB/ODPTBK8i+u4gwrD1UAsVAHuIbOpQu4LYAujluCu5cl2A8
|
|
26
|
+
HhmlCT7sTkqG
|
|
27
27
|
-----END CERTIFICATE-----
|