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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.rubocop.yml +26 -7
- data/.ruby-version +1 -1
- data/CHANGELOG.md +58 -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 -1313
- 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 -419
- 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
data/lib/json_p3/patch.rb
CHANGED
|
@@ -1,329 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "English"
|
|
4
|
-
require_relative "errors"
|
|
5
4
|
|
|
6
5
|
module JSONP3
|
|
7
|
-
#
|
|
8
|
-
class
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
raise "JSON Patch operations must implement #name"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Apply the patch operation to _value_.
|
|
15
|
-
def apply(_value, _index)
|
|
16
|
-
raise "JSON Patch operations must implement apply(value, index)"
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Return a JSON-like representation of this patch operation.
|
|
20
|
-
def to_h
|
|
21
|
-
raise "JSON Patch operations must implement #to_h"
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# The JSON Patch _add_ operation.
|
|
26
|
-
class OpAdd < Op
|
|
27
|
-
# @param pointer [JSONPointer]
|
|
28
|
-
# @param value [JSON-like value]
|
|
29
|
-
def initialize(pointer, value)
|
|
30
|
-
super()
|
|
31
|
-
@pointer = pointer
|
|
32
|
-
@value = value
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def name
|
|
36
|
-
"add"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def apply(value, index)
|
|
40
|
-
parent, obj = @pointer.resolve_with_parent(value)
|
|
41
|
-
return @value if parent == JSONP3::JSONPointer::UNDEFINED && @pointer.tokens.empty?
|
|
42
|
-
|
|
43
|
-
if parent == JSONP3::JSONPointer::UNDEFINED
|
|
44
|
-
raise JSONPatchError,
|
|
45
|
-
"no such property or item '#{@pointer.parent}' (#{name}:#{index})"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
target = @pointer.tokens.last
|
|
49
|
-
if parent.is_a?(Array)
|
|
50
|
-
if obj == JSONP3::JSONPointer::UNDEFINED
|
|
51
|
-
raise JSONPatchError, "index out of range (#{name}:#{index})" unless target == "-"
|
|
52
|
-
|
|
53
|
-
parent << @value
|
|
54
|
-
else
|
|
55
|
-
parent.insert(target.to_i, @value)
|
|
56
|
-
end
|
|
57
|
-
elsif parent.is_a?(Hash)
|
|
58
|
-
parent[target] = @value
|
|
59
|
-
else
|
|
60
|
-
raise JSONPatchError, "unexpected operation on #{parent.class} (#{name}:#{index})"
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
value
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def to_h
|
|
67
|
-
{ "op" => name, "path" => @pointer.to_s, "value" => @value }
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# The JSON Patch _remove_ operation.
|
|
72
|
-
class OpRemove < Op
|
|
73
|
-
# @param pointer [JSONPointer]
|
|
74
|
-
def initialize(pointer)
|
|
75
|
-
super()
|
|
76
|
-
@pointer = pointer
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def name
|
|
80
|
-
"remove"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def apply(value, index)
|
|
84
|
-
parent, obj = @pointer.resolve_with_parent(value)
|
|
85
|
-
|
|
86
|
-
if parent == JSONP3::JSONPointer::UNDEFINED && @pointer.tokens.empty?
|
|
87
|
-
raise JSONPatchError,
|
|
88
|
-
"can't remove root (#{name}:#{index})"
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
if parent == JSONP3::JSONPointer::UNDEFINED
|
|
92
|
-
raise JSONPatchError,
|
|
93
|
-
"no such property or item '#{@pointer.parent}' (#{name}:#{index})"
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
target = @pointer.tokens.last
|
|
97
|
-
if target == JSONP3::JSONPointer::UNDEFINED
|
|
98
|
-
raise JSONPatchError,
|
|
99
|
-
"unexpected operation (#{name}:#{index})"
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
if parent.is_a?(Array)
|
|
103
|
-
raise JSONPatchError, "no item to remove (#{name}:#{index})" if obj == JSONP3::JSONPointer::UNDEFINED
|
|
104
|
-
|
|
105
|
-
parent.delete_at(target.to_i)
|
|
106
|
-
elsif parent.is_a?(Hash)
|
|
107
|
-
raise JSONPatchError, "no property to remove (#{name}:#{index})" if obj == JSONP3::JSONPointer::UNDEFINED
|
|
108
|
-
|
|
109
|
-
parent.delete(target)
|
|
110
|
-
else
|
|
111
|
-
raise JSONPatchError, "unexpected operation on #{parent.class} (#{name}:#{index})"
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
value
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def to_h
|
|
118
|
-
{ "op" => name, "path" => @pointer.to_s }
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# The JSON Patch _replace_ operation.
|
|
123
|
-
class OpReplace < Op
|
|
124
|
-
# @param pointer [JSONPointer]
|
|
125
|
-
# @param value [JSON-like value]
|
|
126
|
-
def initialize(pointer, value)
|
|
127
|
-
super()
|
|
128
|
-
@pointer = pointer
|
|
129
|
-
@value = value
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def name
|
|
133
|
-
"replace"
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
def apply(value, index)
|
|
137
|
-
parent, obj = @pointer.resolve_with_parent(value)
|
|
138
|
-
return @value if parent == JSONP3::JSONPointer::UNDEFINED && @pointer.tokens.empty?
|
|
139
|
-
|
|
140
|
-
if parent == JSONP3::JSONPointer::UNDEFINED
|
|
141
|
-
raise JSONPatchError,
|
|
142
|
-
"no such property or item '#{@pointer.parent}' (#{name}:#{index})"
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
target = @pointer.tokens.last
|
|
146
|
-
if target == JSONP3::JSONPointer::UNDEFINED
|
|
147
|
-
raise JSONPatchError,
|
|
148
|
-
"unexpected operation (#{name}:#{index})"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
if parent.is_a?(Array)
|
|
152
|
-
raise JSONPatchError, "no item to replace (#{name}:#{index})" if obj == JSONP3::JSONPointer::UNDEFINED
|
|
153
|
-
|
|
154
|
-
parent[target.to_i] = @value
|
|
155
|
-
elsif parent.is_a?(Hash)
|
|
156
|
-
raise JSONPatchError, "no property to replace (#{name}:#{index})" if obj == JSONP3::JSONPointer::UNDEFINED
|
|
157
|
-
|
|
158
|
-
parent[target] = @value
|
|
159
|
-
else
|
|
160
|
-
raise JSONPatchError, "unexpected operation on #{parent.class} (#{name}:#{index})"
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
value
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def to_h
|
|
167
|
-
{ "op" => name, "path" => @pointer.to_s, "value" => @value }
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# The JSON Patch _move_ operation.
|
|
172
|
-
class OpMove < Op
|
|
173
|
-
# @param from [JSONPointer]
|
|
174
|
-
# @param pointer [JSONPointer]
|
|
175
|
-
def initialize(from, pointer)
|
|
176
|
-
super()
|
|
177
|
-
@from = from
|
|
178
|
-
@pointer = pointer
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def name
|
|
182
|
-
"move"
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def apply(value, index)
|
|
186
|
-
if @pointer.relative_to?(@from)
|
|
187
|
-
raise JSONPatchError,
|
|
188
|
-
"can't move object to one of its children (#{name}:#{index})"
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Grab the source value.
|
|
192
|
-
source_parent, source_obj = @from.resolve_with_parent(value)
|
|
193
|
-
if source_obj == JSONP3::JSONPointer::UNDEFINED
|
|
194
|
-
raise JSONPatchError,
|
|
195
|
-
"source object does not exist (#{name}:#{index})"
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
source_target = @from.tokens.last
|
|
199
|
-
if source_target == JSONP3::JSONPointer::UNDEFINED
|
|
200
|
-
raise JSONPatchError,
|
|
201
|
-
"unexpected operation (#{name}:#{index})"
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
# Delete the target value from the source location.
|
|
205
|
-
if source_parent.is_a?(Array)
|
|
206
|
-
source_parent.delete_at(source_target.to_i)
|
|
207
|
-
elsif source_parent.is_a?(Hash)
|
|
208
|
-
source_parent.delete(source_target)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Find the parent of the destination pointer.
|
|
212
|
-
dest_parent, _dest_obj = @pointer.resolve_with_parent(value)
|
|
213
|
-
return source_obj if dest_parent == JSONP3::JSONPointer::UNDEFINED
|
|
214
|
-
|
|
215
|
-
dest_target = @pointer.tokens.last
|
|
216
|
-
if dest_target == JSONP3::JSONPointer::UNDEFINED
|
|
217
|
-
raise JSONPatchError,
|
|
218
|
-
"unexpected operation (#{name}:#{index})"
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
# Write the source value to the destination.
|
|
222
|
-
if dest_parent.is_a?(Array)
|
|
223
|
-
if dest_target == "-"
|
|
224
|
-
dest_parent << source_obj
|
|
225
|
-
else
|
|
226
|
-
dest_parent[dest_target.to_i] = source_obj
|
|
227
|
-
end
|
|
228
|
-
elsif dest_parent.is_a?(Hash)
|
|
229
|
-
dest_parent[dest_target] = source_obj
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
value
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def to_h
|
|
236
|
-
{ "op" => name, "from" => @from.to_s, "path" => @pointer.to_s }
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# The JSON Patch _copy_ operation.
|
|
241
|
-
class OpCopy < Op
|
|
242
|
-
# @param from [JSONPointer]
|
|
243
|
-
# @param pointer [JSONPointer]
|
|
244
|
-
def initialize(from, pointer)
|
|
245
|
-
super()
|
|
246
|
-
@from = from
|
|
247
|
-
@pointer = pointer
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def name
|
|
251
|
-
"copy"
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
def apply(value, index)
|
|
255
|
-
# Grab the source value.
|
|
256
|
-
_source_parent, source_obj = @from.resolve_with_parent(value)
|
|
257
|
-
if source_obj == JSONP3::JSONPointer::UNDEFINED
|
|
258
|
-
raise JSONPatchError,
|
|
259
|
-
"source object does not exist (#{name}:#{index})"
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
# Find the parent of the destination pointer.
|
|
263
|
-
dest_parent, _dest_obj = @pointer.resolve_with_parent(value)
|
|
264
|
-
return deep_copy(source_obj) if dest_parent == JSONP3::JSONPointer::UNDEFINED
|
|
265
|
-
|
|
266
|
-
dest_target = @pointer.tokens.last
|
|
267
|
-
if dest_target == JSONP3::JSONPointer::UNDEFINED
|
|
268
|
-
raise JSONPatchError,
|
|
269
|
-
"unexpected operation (#{name}:#{index})"
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
# Write the source value to the destination.
|
|
273
|
-
if dest_parent.is_a?(Array)
|
|
274
|
-
if dest_target == "-"
|
|
275
|
-
dest_parent << source_obj
|
|
276
|
-
else
|
|
277
|
-
dest_parent.insert(dest_target.to_i, deep_copy(source_obj))
|
|
278
|
-
end
|
|
279
|
-
elsif dest_parent.is_a?(Hash)
|
|
280
|
-
dest_parent[dest_target] = deep_copy(source_obj)
|
|
281
|
-
else
|
|
282
|
-
raise JSONPatchError, "unexpected operation on #{dest_parent.class} (#{name}:#{index})"
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
value
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
def to_h
|
|
289
|
-
{ "op" => name, "from" => @from.to_s, "path" => @pointer.to_s }
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
private
|
|
293
|
-
|
|
294
|
-
def deep_copy(obj)
|
|
295
|
-
Marshal.load(Marshal.dump(obj))
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
# The JSON Patch _test_ operation.
|
|
300
|
-
class OpTest < Op
|
|
301
|
-
# @param pointer [JSONPointer]
|
|
302
|
-
# @param value [JSON-like value]
|
|
303
|
-
def initialize(pointer, value)
|
|
304
|
-
super()
|
|
305
|
-
@pointer = pointer
|
|
306
|
-
@value = value
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def name
|
|
310
|
-
"test"
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
def apply(value, index)
|
|
314
|
-
obj = @pointer.resolve(value)
|
|
315
|
-
raise JSONPatchTestFailure, "test failed (#{name}:#{index})" if obj != @value
|
|
316
|
-
|
|
317
|
-
value
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def to_h
|
|
321
|
-
{ "op" => name, "path" => @pointer.to_s, "value" => @value }
|
|
6
|
+
# A JSON Patch containing zero or more patch operations.
|
|
7
|
+
class Patch
|
|
8
|
+
def self.apply!(ops, value)
|
|
9
|
+
new(ops).apply!(value)
|
|
322
10
|
end
|
|
323
|
-
end
|
|
324
11
|
|
|
325
|
-
# A JSON Patch containing zero or more patch operations.
|
|
326
|
-
class JSONPatch
|
|
327
12
|
# @param ops [Array<Op | Hash<String, untyped>>?]
|
|
328
13
|
def initialize(ops = nil)
|
|
329
14
|
@ops = []
|
|
@@ -384,8 +69,8 @@ module JSONP3
|
|
|
384
69
|
end
|
|
385
70
|
|
|
386
71
|
# Apply this patch to JSON-like value _value_.
|
|
387
|
-
def apply(value)
|
|
388
|
-
@ops.each_with_index { |op, i| value = op.apply(value, i) }
|
|
72
|
+
def apply!(value)
|
|
73
|
+
@ops.each_with_index { |op, i| value = op.apply!(value, i) }
|
|
389
74
|
value
|
|
390
75
|
end
|
|
391
76
|
|
|
@@ -418,22 +103,22 @@ module JSONP3
|
|
|
418
103
|
when "test"
|
|
419
104
|
test(op_pointer(obj, "path", "test", i), op_value(obj, "value", "test", i))
|
|
420
105
|
else
|
|
421
|
-
raise
|
|
106
|
+
raise Patch::Error,
|
|
422
107
|
"expected 'op' to be one of 'add', 'remove', 'replace', 'move', 'copy' or 'test' (#{obj["op"]}:#{i})"
|
|
423
108
|
end
|
|
424
109
|
end
|
|
425
110
|
end
|
|
426
111
|
|
|
427
112
|
def op_pointer(obj, key, op, index)
|
|
428
|
-
raise
|
|
113
|
+
raise Patch::Error, "missing property '#{key}' (#{op}:#{index})" unless obj.key?(key)
|
|
429
114
|
|
|
430
|
-
JSONP3::
|
|
431
|
-
rescue
|
|
432
|
-
raise
|
|
115
|
+
JSONP3::Pointer.new(obj[key])
|
|
116
|
+
rescue Pointer::Error
|
|
117
|
+
raise Patch::Error, "#{$ERROR_INFO} (#{op}:#{index})"
|
|
433
118
|
end
|
|
434
119
|
|
|
435
120
|
def op_value(obj, key, op, index)
|
|
436
|
-
raise
|
|
121
|
+
raise Patch::Error, "missing property '#{key}' (#{op}:#{index})" unless obj.key?(key)
|
|
437
122
|
|
|
438
123
|
obj[key]
|
|
439
124
|
end
|
|
@@ -441,9 +126,9 @@ module JSONP3
|
|
|
441
126
|
def ensure_pointer(pointer, op, index)
|
|
442
127
|
return pointer unless pointer.is_a?(String)
|
|
443
128
|
|
|
444
|
-
JSONP3::
|
|
445
|
-
rescue
|
|
446
|
-
raise
|
|
129
|
+
JSONP3::Pointer.new(pointer)
|
|
130
|
+
rescue Pointer::Error
|
|
131
|
+
raise Patch::Error, "#{$ERROR_INFO} (#{op}:#{index})"
|
|
447
132
|
end
|
|
448
133
|
end
|
|
449
134
|
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lexer"
|
|
4
|
+
require_relative "parser"
|
|
5
|
+
require_relative "query"
|
|
6
|
+
require_relative "function"
|
|
7
|
+
require_relative "function_extensions/length"
|
|
8
|
+
require_relative "function_extensions/value"
|
|
9
|
+
require_relative "function_extensions/count"
|
|
10
|
+
require_relative "function_extensions/match"
|
|
11
|
+
require_relative "function_extensions/search"
|
|
12
|
+
|
|
13
|
+
module JSONP3
|
|
14
|
+
module Path
|
|
15
|
+
# JSONPath configuration
|
|
16
|
+
#
|
|
17
|
+
# Configure an environment by inheriting from `Environment` and setting one
|
|
18
|
+
# or more constants and/or overriding {setup_function_extensions}.
|
|
19
|
+
class Environment
|
|
20
|
+
# The maximum integer allowed when selecting array items by index.
|
|
21
|
+
MAX_INT_INDEX = (2**53) - 1
|
|
22
|
+
|
|
23
|
+
# The minimum integer allowed when selecting array items by index.
|
|
24
|
+
MIN_INT_INDEX = -(2**53) + 1
|
|
25
|
+
|
|
26
|
+
# The maximum number of arrays and hashes the descendent segment will traverse
|
|
27
|
+
# before raising a {JSONPathRecursionError}.
|
|
28
|
+
MAX_RECURSION_DEPTH = 100
|
|
29
|
+
|
|
30
|
+
# One of the available implementations of the _name selector_.
|
|
31
|
+
#
|
|
32
|
+
# - {NameSelector} (the default) will select values from hashes using string keys.
|
|
33
|
+
# - {SymbolNameSelector} will select values from hashes using string or symbol keys.
|
|
34
|
+
#
|
|
35
|
+
# Implement your own name selector by inheriting from {NameSelector} and overriding
|
|
36
|
+
# `#resolve`.
|
|
37
|
+
NAME_SELECTOR = NameSelector
|
|
38
|
+
|
|
39
|
+
# An implementation of the _index selector_. The default implementation will
|
|
40
|
+
# select values from arrays only. Implement your own by inheriting from
|
|
41
|
+
# {IndexSelector} and overriding `#resolve`.
|
|
42
|
+
INDEX_SELECTOR = IndexSelector
|
|
43
|
+
|
|
44
|
+
attr_accessor :function_extensions
|
|
45
|
+
|
|
46
|
+
def initialize
|
|
47
|
+
@function_extensions = {}
|
|
48
|
+
setup_function_extensions
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Prepare JSONPath expression _query_ for repeated application.
|
|
52
|
+
# @param query [String]
|
|
53
|
+
# @return [Query]
|
|
54
|
+
def compile(query)
|
|
55
|
+
tokens = JSONP3::Path.tokenize(query)
|
|
56
|
+
Query.new(self, Parser.new(self, query, tokens).parse)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Apply JSONPath expression _query_ to _value_.
|
|
60
|
+
# @param query [String] the JSONPath expression
|
|
61
|
+
# @param value [JSON-like data] the target JSON "document"
|
|
62
|
+
# @return [Array<JSONPath>]
|
|
63
|
+
def find(query, value)
|
|
64
|
+
compile(query).find(value)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Apply JSONPath expression _query_ to _value_.
|
|
68
|
+
# @param query [String] the JSONPath expression
|
|
69
|
+
# @param value [JSON-like data] the target JSON "document"
|
|
70
|
+
# @return [Enumerable<JSONPath>]
|
|
71
|
+
def find_enum(query, value)
|
|
72
|
+
compile(query).find_enum(value)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Apply JSONPath expression _query_ to _value_ an return the first
|
|
76
|
+
# available node.
|
|
77
|
+
# @param query [String] the JSONPath expression
|
|
78
|
+
# @param value [JSON-like data] the target JSON "document"
|
|
79
|
+
# @return [JSONPathNode | nil]
|
|
80
|
+
def match(path, value)
|
|
81
|
+
find_enum(path, value).first
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Apply JSONPath expression _query_ to _value_ an return `true` if there's at
|
|
85
|
+
# least one node, or nil if there were no matches.
|
|
86
|
+
# @param query [String] the JSONPath expression
|
|
87
|
+
# @param value [JSON-like data] the target JSON "document"
|
|
88
|
+
# @return [bool]
|
|
89
|
+
def match?(path, value)
|
|
90
|
+
!find_enum(path, value).first.nil?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Apply JSONPath expression _query_ to _value_ an return the first
|
|
94
|
+
# available node, or nil if there were no matches.
|
|
95
|
+
# @param query [String] the JSONPath expression
|
|
96
|
+
# @param value [JSON-like data] the target JSON "document"
|
|
97
|
+
# @return [JSONPathNode | nil]
|
|
98
|
+
def first(path, value)
|
|
99
|
+
find_enum(path, value).first
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Override this function to configure JSONPath function extensions.
|
|
103
|
+
# By default, only the standard functions described in RFC 9535 are enabled.
|
|
104
|
+
def setup_function_extensions
|
|
105
|
+
@function_extensions["length"] = Length.new
|
|
106
|
+
@function_extensions["count"] = Count.new
|
|
107
|
+
@function_extensions["value"] = Value.new
|
|
108
|
+
@function_extensions["match"] = Match.new
|
|
109
|
+
@function_extensions["search"] = Search.new
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|