json_p3 0.4.0 → 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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.rubocop.yml +26 -7
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +58 -0
  6. data/README.md +125 -123
  7. data/Rakefile +3 -3
  8. data/certs/jgrp.pem +21 -21
  9. data/lib/json_p3/errors.rb +51 -43
  10. data/lib/json_p3/patch/op.rb +23 -0
  11. data/lib/json_p3/patch/op_add.rb +51 -0
  12. data/lib/json_p3/patch/op_copy.rb +64 -0
  13. data/lib/json_p3/patch/op_move.rb +74 -0
  14. data/lib/json_p3/patch/op_remove.rb +56 -0
  15. data/lib/json_p3/patch/op_replace.rb +54 -0
  16. data/lib/json_p3/patch/op_test.rb +31 -0
  17. data/lib/json_p3/patch.rb +15 -330
  18. data/lib/json_p3/path/environment.rb +113 -0
  19. data/lib/json_p3/path/filter.rb +463 -0
  20. data/lib/json_p3/path/function.rb +12 -0
  21. data/lib/json_p3/path/function_extensions/count.rb +15 -0
  22. data/lib/json_p3/path/function_extensions/length.rb +17 -0
  23. data/lib/json_p3/path/function_extensions/match.rb +62 -0
  24. data/lib/json_p3/path/function_extensions/pattern.rb +42 -0
  25. data/lib/json_p3/path/function_extensions/search.rb +44 -0
  26. data/lib/json_p3/path/function_extensions/value.rb +15 -0
  27. data/lib/json_p3/path/lexer.rb +220 -0
  28. data/lib/json_p3/path/node.rb +48 -0
  29. data/lib/json_p3/path/parser.rb +676 -0
  30. data/lib/json_p3/path/query.rb +74 -0
  31. data/lib/json_p3/path/segment.rb +172 -0
  32. data/lib/json_p3/path/selector.rb +304 -0
  33. data/lib/json_p3/path/serialize.rb +16 -0
  34. data/lib/json_p3/path/unescape.rb +134 -0
  35. data/lib/json_p3/pointer.rb +15 -76
  36. data/lib/json_p3/relative_pointer.rb +69 -0
  37. data/lib/json_p3/version.rb +1 -1
  38. data/lib/json_p3.rb +50 -13
  39. data/sig/json_p3/cache.rbs +21 -0
  40. data/sig/json_p3/errors.rbs +55 -0
  41. data/sig/json_p3/patch.rbs +145 -0
  42. data/sig/json_p3/path/environment.rbs +81 -0
  43. data/sig/json_p3/path/filter.rbs +196 -0
  44. data/sig/json_p3/path/function.rbs +94 -0
  45. data/sig/json_p3/path/lexer.rbs +62 -0
  46. data/sig/json_p3/path/node.rbs +46 -0
  47. data/sig/json_p3/path/parser.rbs +92 -0
  48. data/sig/json_p3/path/query.rbs +47 -0
  49. data/sig/json_p3/path/segment.rbs +54 -0
  50. data/sig/json_p3/path/selector.rbs +100 -0
  51. data/sig/json_p3/path/serialize.rbs +9 -0
  52. data/sig/json_p3/path/unescape.rbs +12 -0
  53. data/sig/json_p3/pointer.rbs +64 -0
  54. data/sig/json_p3/relative_pointer.rbs +30 -0
  55. data/sig/json_p3.rbs +24 -1313
  56. data.tar.gz.sig +0 -0
  57. metadata +66 -46
  58. metadata.gz.sig +0 -0
  59. data/lib/json_p3/environment.rb +0 -111
  60. data/lib/json_p3/filter.rb +0 -459
  61. data/lib/json_p3/function.rb +0 -10
  62. data/lib/json_p3/function_extensions/count.rb +0 -15
  63. data/lib/json_p3/function_extensions/length.rb +0 -17
  64. data/lib/json_p3/function_extensions/match.rb +0 -62
  65. data/lib/json_p3/function_extensions/pattern.rb +0 -39
  66. data/lib/json_p3/function_extensions/search.rb +0 -44
  67. data/lib/json_p3/function_extensions/value.rb +0 -15
  68. data/lib/json_p3/lexer.rb +0 -419
  69. data/lib/json_p3/node.rb +0 -44
  70. data/lib/json_p3/parser.rb +0 -553
  71. data/lib/json_p3/path.rb +0 -72
  72. data/lib/json_p3/segment.rb +0 -158
  73. data/lib/json_p3/selector.rb +0 -306
  74. data/lib/json_p3/serialize.rb +0 -13
  75. data/lib/json_p3/token.rb +0 -36
  76. data/lib/json_p3/unescape.rb +0 -112
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db1ec71125b99bc877ba9856434a65e3f8e1c11a48b9566443c3b1a8b6860169
4
- data.tar.gz: ad176fdda64b4f741e9f86ab48d4c8e3ccd767187fc0fc0fe28a4e6967a071d2
3
+ metadata.gz: 1c8afa8205821093144f9c2ccbe427e7d726251e48e8cc1480152d0efc8ade7c
4
+ data.tar.gz: 77639923975a6f89d10cbad38891c59015f06797f084ccaacc78073a5e4161ed
5
5
  SHA512:
6
- metadata.gz: 3b153a665f534891e1ed1976ca6c180cd1c2427786b9f7b74fcea1953d7337135db669b3541d5eaba7fd2829dbd6c94e6c36236a4a6c6221d1bd5c0ac45f5a04
7
- data.tar.gz: 52303daa1cd27c3f3ed7187949f6b9a2cf2c95483e61cd8cf7ced438f5511ad9067f39496b1fe53a85932a3de2be15ebfa2c791fdbc9002aceb0c85395c04839
6
+ metadata.gz: 178a01fe4d486b0d2787d46edbe93dce36a6fe6b2b30410daaa57f44da385649aad995c1187ac55253d8cdbe2448f86c73bdd56f1142ba066364c24cd9a2dfe9
7
+ data.tar.gz: 82d38fc46b9d1049135e79d1b3b2aae814ef65285153f5b57242021b7b9b5da5f55f2b5c092cd0e301b6f5ef0b047d59d354bb998c92fe522ecdb2804bc688e9
checksums.yaml.gz.sig CHANGED
Binary file
data/.rubocop.yml CHANGED
@@ -1,10 +1,10 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-minitest
3
3
  - rubocop-rake
4
4
  - rubocop-performance
5
5
 
6
6
  AllCops:
7
- TargetRubyVersion: 3.0
7
+ TargetRubyVersion: 3.3
8
8
  NewCops: enable
9
9
 
10
10
  Style/StringLiterals:
@@ -13,21 +13,40 @@ Style/StringLiterals:
13
13
  Style/StringLiteralsInInterpolation:
14
14
  EnforcedStyle: double_quotes
15
15
 
16
+ Style/ComparableBetween:
17
+ Enabled: false
18
+
19
+ Layout/LeadingCommentSpace:
20
+ Enabled: false
21
+
16
22
  Metrics/AbcSize:
17
- Max: 50
23
+ Enabled: false
18
24
 
19
25
  Metrics/ClassLength:
20
- Max: 500
26
+ Enabled: false
21
27
 
22
28
  Metrics/CyclomaticComplexity:
23
- Max: 20
29
+ Enabled: false
24
30
 
25
31
  Metrics/MethodLength:
26
- Max: 50
32
+ Enabled: false
27
33
 
28
34
  Metrics/PerceivedComplexity:
29
- Max: 20
35
+ Enabled: false
36
+
37
+ Metrics/BlockNesting:
38
+ Max: 4
39
+
40
+ Metrics/BlockLength:
41
+ Enabled: false
42
+
43
+ Metrics/ModuleLength:
44
+ Enabled: false
30
45
 
31
46
  Naming/MethodParameterName:
32
47
  AllowedNames:
33
48
  - op
49
+ - ch
50
+
51
+ Naming/PredicateMethod:
52
+ Enabled: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.6
1
+ 4.0.2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,61 @@
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
+
55
+ ## [0.4.1] - 2025-03-18
56
+
57
+ - Fixed `JSONPathSyntaxError` claiming "unbalanced parentheses" when the query has balanced brackets.
58
+
1
59
  ## [0.4.0] - 2025-02-10
2
60
 
3
61
  - Added `JSONP3.find_enum`, `JSONP3::JSONPathEnvironment.find_enum` and `JSONP3::JSONPath.find_enum`. `find_enum` is like `find`, but returns an Enumerable (usually an Enumerator) of `JSONPathNode` instances instead of a `JSONPathNodeList`. `find_enum` can be more efficient for some combinations of query and data, especially for large data and recursive queries.
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.1%20%7C%203.2%20%7C%203.3%20%7C%203.4-CC342D?style=flat-square">
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.2.1'
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
- "name": "Sue",
75
- "score": 100
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<JSONPathNode>`
140
+ `find: (String query, top value) -> Array[JSONP3::Path::Node]`
148
141
 
149
- Apply JSONPath expression _query_ to JSON-like data _value_. An array of JSONPathNode instances is returned, one node for each value matched by _query_. The returned array will be empty if there were no matches.
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
- Each `JSONPathNode` has:
144
+ For each `Node`:
152
145
 
153
- - a `value` attribute, which is the JSON-like value associated with the node.
154
- - a `location` attribute, which is a nested array of hash/object names and array indices that were required to reach the node's value in the target JSON document.
155
- - a `path()` method, which returns the normalized path to the node in the target JSON document.
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
- "name": "Sue",
166
- "score": 100
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<JSONPathNode>`
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 `JSONPathNode` 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 recursive descent segments.
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) -> JSONPath`
195
+ `compile: (String query) -> JSONP3::Path::Query`
215
196
 
216
- Prepare a JSONPath expression for repeated application to different JSON-like data. An instance of `JSONPath` has a `find(data)` method, which behaves similarly to the module-level `find(query, data)` method.
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
- "name": "Sue",
227
- "score": 100
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) -> JSONPathNode | nil`
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?(query, value) -> bool`
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
- ### JSONPathEnvironment
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 could create your own environment like this:
255
+ You can create your own environment like this:
289
256
 
290
257
  ```ruby
291
258
  require "json_p3"
292
259
 
293
- jsonpath = JSONP3::JSONPathEnvironment.new
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 `JSONPathEnvironment` and override some of its constants or the `#setup_function_extensions` method.
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::JSONPathEnvironment
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
- ### JSONPathError
304
+ ### Errors
338
305
 
339
- `JSONPathError` is the base class for all JSONPath exceptions. The following classes inherit from `JSONPathError` and will only occur when parsing a JSONPath expression, not when applying a path to some data.
306
+ `JSONP3::Error` is the base class for all exceptions raised from this Gem.
340
307
 
341
- - `JSONPathSyntaxError`
342
- - `JSONPathTypeError`
343
- - `JSONPathNameError`
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
- `JSONPathError` implements `#detailed_message`. With recent versions of Ruby you should get useful error messages.
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::JSONPathSyntaxError: unexpected trailing whitespace
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
- "name": "Sue",
370
- "score": 100
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::JSONPointer::UNDEFINED` is returned instead. You can use your own default value using the `default:` keyword argument.
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()`. **Data is modified in place**.
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::JSONPatch.new(ops).apply(value)`. Use the `JSONPatch` constructor when you need to apply the same patch to different data.
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 `JSONPatch`, we offer a builder API to construct JSON Patch documents programmatically.
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::JSONPatch.new
438
- .add("/some/foo", { "foo" => [] })
439
- .add("/some/foo", { "bar" => [] })
440
- .copy("/some/other", "/some/foo/else")
441
- .copy("/some/foo/else", "/some/foo/bar/-")
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.requires << "rubocop-minitest"
12
- task.requires << "rubocop-rake"
13
- task.requires << "rubocop-performance"
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
- FgNjb20wHhcNMjQxMDI0MDcxNDAzWhcNMjUxMDI0MDcxNDAzWjBEMRYwFAYDVQQD
4
+ FgNjb20wHhcNMjUxMDI1MDc0ODI4WhcNMjYxMDI1MDc0ODI4WjBEMRYwFAYDVQQD
5
5
  DA1qYW1lc2dyLnByaW9yMRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
6
- k/IsZAEZFgNjb20wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDvtEeN
7
- ZNN153v2ZK+U+Tj7kfRgl52GzmZS7lIG83xvT5/K8xEG5G4Tqiq/aGuDdbnxtd/t
8
- TQf6CtkixPzwj7WGIwoV28AUHKOJ7UjqRhXIuAn3nmzRLRNK2gVYpvzLTNCyLhIb
9
- jyZqgvtjR9mmW/Uuq+9LcLN4iFfkW+4dSmcABnkZHWMWGZYMIwoYYpluEUS1KiRZ
10
- mIALPEGO5JcpVvjpDwaP6GqV0khgnLixz9KlAF+2DCyki21wxtJoxJTuL9H5Ogb+
11
- k+nDQd9ELFYfbn73UKYGPtoUxkQeNF/ajJAkb/8t0cwUeSgtpSPhWIITWBgdLq11
12
- svViBlK9UZZC+dWG9x1lueVTG9fXBl69NJ9VA7MaxmHVY133sEySqwZMCrtDYjPX
13
- wIzLI2U8iSDIsjxTq3F3pbxeD4ee85ZY/xAV9M6fpjmKJERibRZTUPQo9ujHAhVM
14
- YNbfAHhPRGphT64/ocBjCmonrvA0fB3jP5wduEa5vD3JPOSb7dtCnNzNvDcCAwEA
15
- AaOBgTB/MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBSswivEK7I4
16
- DrAJcSgIMhgedOJm5DAiBgNVHREEGzAZgRdqYW1lc2dyLnByaW9yQGdtYWlsLmNv
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
- AQsFAAOCAYEAdq8q0kDj14XNFMVYdjv1dG6Zaom3hEIkWv7+AsaPVw33puAnffQU
19
- lb2H1FG0LOGailuzPUadop6w/7oHO2jefj/85V0Tk6Hhk8cQpYQrtQgOHA3nsbmJ
20
- 9JksJ+Y8RbyZP5dDTyZjTG4N3kgwZeNozcLeLj2ONBZRvGAjh8fyHOdLMPVNpM2K
21
- naRHKdSqWSU728nHDiVvLlGVkRInDmC+gC1msKqSEnQrLiBl27jMbsce9GKi2cb7
22
- DNjDwDg37yrbWgU5LS97+DU6dbMGVE3zPpuZGPZMTdXqzWWy2VlTPXMvAVt3Y4YG
23
- 90hO1nDIwLn/1w9+Vb76b/5xc8AWWLozK4IPjh3pTvL6tCDiZw7tOQYp2uIO1Id1
24
- hKbb8cJaj/A6UsTE00yz5CUyQMcLz3LaL9k7Ek0u7vCcJLicl1HbAeq1ah7ePLLX
25
- 6dM18fnfBc3yA4KI7AO8UAmRkTscMYV6f/K4YZR6ZYCNWRpY7rkg+arhf05aoSQf
26
- vn9bO1bzwdnG
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-----