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
@@ -1,158 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONP3
4
- # Base class for all JSONPath segments.
5
- class Segment
6
- # @dynamic token, selectors
7
- attr_reader :token, :selectors
8
-
9
- def initialize(env, token, selectors)
10
- @env = env
11
- @token = token
12
- @selectors = selectors
13
- end
14
-
15
- # Select the children of each node in _nodes_.
16
- # @return [Array<JSONPathNode>]
17
- def resolve(_nodes)
18
- raise "segments must implement resolve(nodes)"
19
- end
20
-
21
- # Select the children of each node in _nodes_.
22
- # @return [Enumerable<JSONPathNode>]
23
- def resolve_enum(_nodes)
24
- raise "segments must implement resolve_enum(nodes)"
25
- end
26
- end
27
-
28
- # The child selection segment.
29
- class ChildSegment < Segment
30
- def resolve(nodes)
31
- rv = [] # : Array[JSONPathNode]
32
- nodes.each do |node|
33
- @selectors.each do |selector|
34
- rv.concat selector.resolve(node)
35
- end
36
- end
37
- rv
38
- end
39
-
40
- def resolve_enum(nodes)
41
- Enumerator.new do |yielder|
42
- nodes.each do |node|
43
- @selectors.each do |selector|
44
- selector.resolve(node).each do |item|
45
- yielder << item
46
- end
47
- end
48
- end
49
- end
50
- end
51
-
52
- def to_s
53
- "[#{@selectors.map(&:to_s).join(", ")}]"
54
- end
55
-
56
- def ==(other)
57
- self.class == other.class &&
58
- @selectors == other.selectors &&
59
- @token == other.token
60
- end
61
-
62
- alias eql? ==
63
-
64
- def hash
65
- [@selectors, @token].hash
66
- end
67
- end
68
-
69
- # The recursive descent segment
70
- class RecursiveDescentSegment < Segment
71
- def resolve(nodes)
72
- rv = [] # : Array[JSONPathNode]
73
- nodes.each do |node|
74
- visit(node).each do |descendant|
75
- @selectors.each do |selector|
76
- rv.concat selector.resolve(descendant)
77
- end
78
- end
79
- end
80
- rv
81
- end
82
-
83
- def resolve_enum(nodes)
84
- Enumerator.new do |yielder|
85
- nodes.each do |node|
86
- visit_enum(node).each do |descendant|
87
- @selectors.each do |selector|
88
- selector.resolve(descendant).each do |item|
89
- yielder << item
90
- end
91
- end
92
- end
93
- end
94
- end
95
- end
96
-
97
- def to_s
98
- "..[#{@selectors.map(&:to_s).join(", ")}]"
99
- end
100
-
101
- def ==(other)
102
- self.class == other.class &&
103
- @selectors == other.selectors &&
104
- @token == other.token
105
- end
106
-
107
- alias eql? ==
108
-
109
- def hash
110
- ["..", @selectors, @token].hash
111
- end
112
-
113
- protected
114
-
115
- def visit(node, depth = 1)
116
- raise JSONPathRecursionError.new("recursion limit exceeded", @token) if depth > @env.class::MAX_RECURSION_DEPTH
117
-
118
- rv = [node]
119
-
120
- if node.value.is_a? Array
121
- node.value.each_with_index do |value, i|
122
- child = JSONPathNode.new(value, [node.location, i], node.root)
123
- rv.concat visit(child, depth + 1)
124
- end
125
- elsif node.value.is_a? Hash
126
- node.value.each do |key, value|
127
- child = JSONPathNode.new(value, [node.location, key], node.root)
128
- rv.concat visit(child, depth + 1)
129
- end
130
- end
131
-
132
- rv
133
- end
134
-
135
- def visit_enum(node, depth = 1)
136
- raise JSONPathRecursionError.new("recursion limit exceeded", @token) if depth > @env.class::MAX_RECURSION_DEPTH
137
-
138
- Enumerator.new do |yielder|
139
- yielder << node
140
- if node.value.is_a? Array
141
- node.value.each_with_index do |value, i|
142
- child = JSONPathNode.new(value, [node.location, i], node.root)
143
- visit_enum(child, depth + 1).each do |item|
144
- yielder << item
145
- end
146
- end
147
- elsif node.value.is_a? Hash
148
- node.value.each do |key, value|
149
- child = JSONPathNode.new(value, [node.location, key], node.root)
150
- visit_enum(child, depth + 1).each do |item|
151
- yielder << item
152
- end
153
- end
154
- end
155
- end
156
- end
157
- end
158
- end
@@ -1,306 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONP3
4
- # Base class for all JSONPath selectors
5
- class Selector
6
- # @dynamic token
7
- attr_reader :token
8
-
9
- def initialize(env, token)
10
- @env = env
11
- @token = token
12
- end
13
-
14
- # Apply this selector to _node_.
15
- # @return [Array<JSONPathNode>]
16
- def resolve(_node)
17
- raise "selectors must implement resolve(node)"
18
- end
19
-
20
- # Apply this selector to _node_.
21
- # @return [Enumerable<JSONPathNode>]
22
- def resolve_enum(node)
23
- resolve(node)
24
- end
25
-
26
- # Return true if this selector is a singular selector.
27
- def singular?
28
- false
29
- end
30
- end
31
-
32
- # The name selector select values from hashes given a key.
33
- class NameSelector < Selector
34
- # @dynamic name
35
- attr_reader :name
36
-
37
- def initialize(env, token, name)
38
- super(env, token)
39
- @name = name
40
- end
41
-
42
- def resolve(node)
43
- if node.value.is_a?(Hash) && node.value.key?(@name)
44
- [node.new_child(node.value[@name], @name)]
45
- else
46
- []
47
- end
48
- end
49
-
50
- def singular?
51
- true
52
- end
53
-
54
- def to_s
55
- JSONP3.canonical_string(@name)
56
- end
57
-
58
- def ==(other)
59
- self.class == other.class &&
60
- @name == other.name &&
61
- @token == other.token
62
- end
63
-
64
- alias eql? ==
65
-
66
- def hash
67
- [@name, @token].hash
68
- end
69
- end
70
-
71
- # This non-standard name selector selects values from hashes given a string or
72
- # symbol key.
73
- class SymbolNameSelector < NameSelector
74
- def initialize(env, token, name)
75
- super
76
- @sym = @name.to_sym
77
- end
78
-
79
- def resolve(node)
80
- if node.value.is_a?(Hash)
81
- if node.value.key?(@name)
82
- [node.new_child(node.value[@name], @name)]
83
- elsif node.value.key?(@sym)
84
- [node.new_child(node.value[@sym], @name)]
85
- else
86
- []
87
- end
88
- else
89
- []
90
- end
91
- end
92
- end
93
-
94
- # The index selector selects values from arrays given an index.
95
- class IndexSelector < Selector
96
- # @dynamic index
97
- attr_reader :index
98
-
99
- def initialize(env, token, index)
100
- super(env, token)
101
- @index = index
102
- end
103
-
104
- def resolve(node)
105
- if node.value.is_a?(Array)
106
- norm_index = normalize(@index, node.value.length)
107
- return [] if norm_index.negative? || norm_index >= node.value.length
108
-
109
- [node.new_child(node.value[@index], norm_index)]
110
- else
111
- []
112
- end
113
- end
114
-
115
- def singular?
116
- true
117
- end
118
-
119
- def to_s
120
- @index.to_s
121
- end
122
-
123
- def ==(other)
124
- self.class == other.class &&
125
- @index == other.index &&
126
- @token == other.token
127
- end
128
-
129
- alias eql? ==
130
-
131
- def hash
132
- [@index, @token].hash
133
- end
134
-
135
- private
136
-
137
- def normalize(index, length)
138
- index.negative? && length >= index.abs ? length + index : index
139
- end
140
- end
141
-
142
- # The wildcard selector selects all elements from an array or values from a hash.
143
- class WildcardSelector < Selector
144
- def resolve(node)
145
- if node.value.is_a? Hash
146
- node.value.map { |k, v| node.new_child(v, k) }
147
- elsif node.value.is_a? Array
148
- node.value.map.with_index { |e, i| node.new_child(e, i) }
149
- else
150
- []
151
- end
152
- end
153
-
154
- def resolve_enum(node)
155
- if node.value.is_a? Hash
156
- Enumerator.new do |yielder|
157
- node.value.each do |k, v|
158
- yielder << node.new_child(v, k)
159
- end
160
- end
161
- elsif node.value.is_a? Array
162
- Enumerator.new do |yielder|
163
- node.value.each.with_index do |e, i|
164
- yielder << node.new_child(e, i)
165
- end
166
- end
167
- else
168
- []
169
- end
170
- end
171
-
172
- def to_s
173
- "*"
174
- end
175
-
176
- def ==(other)
177
- self.class == other.class && @token == other.token
178
- end
179
-
180
- alias eql? ==
181
-
182
- def hash
183
- @token.hash
184
- end
185
- end
186
-
187
- # The slice selector selects a range of elements from an array.
188
- class SliceSelector < Selector
189
- # @dynamic start, stop, step
190
- attr_reader :start, :stop, :step
191
-
192
- def initialize(env, token, start, stop, step)
193
- super(env, token)
194
- @start = start
195
- @stop = stop
196
- @step = step || 1
197
- end
198
-
199
- def resolve(node)
200
- return [] unless node.value.is_a?(Array)
201
-
202
- length = node.value.length
203
- return [] if length.zero? || @step.zero?
204
-
205
- normalized_start = if @start.nil?
206
- @step.negative? ? length - 1 : 0
207
- elsif @start&.negative?
208
- [length + (@start || raise), 0].max
209
- else
210
- [@start || raise, length - 1].min
211
- end
212
-
213
- normalized_stop = if @stop.nil?
214
- @step.negative? ? -1 : length
215
- elsif @stop&.negative?
216
- [length + (@stop || raise), -1].max
217
- else
218
- [@stop || raise, length].min
219
- end
220
-
221
- (normalized_start...normalized_stop).step(@step).map { |i| node.new_child(node.value[i], i) }
222
- end
223
-
224
- def to_s
225
- start = @start || ""
226
- stop = @stop || ""
227
- step = @step || 1
228
- "#{start}:#{stop}:#{step}"
229
- end
230
-
231
- def ==(other)
232
- self.class == other.class &&
233
- @start == other.start &&
234
- @stop == other.stop &&
235
- @step == other.step &&
236
- @token == other.token
237
- end
238
-
239
- alias eql? ==
240
-
241
- def hash
242
- [@start, @stop, @step, @token].hash
243
- end
244
- end
245
-
246
- # Select array elements or hash values according to a filter expression.
247
- class FilterSelector < Selector
248
- # @dynamic expression
249
- attr_reader :expression
250
-
251
- def initialize(env, token, expression)
252
- super(env, token)
253
- @expression = expression
254
- end
255
-
256
- def resolve(node)
257
- nodes = [] # : Array[JSONPathNode]
258
-
259
- if node.value.is_a?(Array)
260
- node.value.each_with_index do |e, i|
261
- context = FilterContext.new(@env, e, node.root)
262
- nodes << node.new_child(e, i) if @expression.evaluate(context)
263
- end
264
- elsif node.value.is_a?(Hash)
265
- node.value.each_pair do |k, v|
266
- context = FilterContext.new(@env, v, node.root)
267
- nodes << node.new_child(v, k) if @expression.evaluate(context)
268
- end
269
- end
270
-
271
- nodes
272
- end
273
-
274
- def resolve_enum(node)
275
- Enumerator.new do |yielder|
276
- if node.value.is_a?(Array)
277
- node.value.each_with_index do |e, i|
278
- context = FilterContext.new(@env, e, node.root)
279
- yielder << node.new_child(e, i) if @expression.evaluate(context)
280
- end
281
- elsif node.value.is_a?(Hash)
282
- node.value.each_pair do |k, v|
283
- context = FilterContext.new(@env, v, node.root)
284
- yielder << node.new_child(v, k) if @expression.evaluate(context)
285
- end
286
- end
287
- end
288
- end
289
-
290
- def to_s
291
- "?#{@expression}"
292
- end
293
-
294
- def ==(other)
295
- self.class == other.class &&
296
- @expression == other.start &&
297
- @token == other.token
298
- end
299
-
300
- alias eql? ==
301
-
302
- def hash
303
- [@expression, @token].hash
304
- end
305
- end
306
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
-
5
- module JSONP3 # rubocop:disable Style/Documentation
6
- TRANS = { "\\\"" => "\"", "'" => "\\'" }.freeze
7
-
8
- # Return _value_ formatted as a canonical string literal.
9
- # @param value [String]
10
- def self.canonical_string(value)
11
- "'#{(JSON.dump(value)[1..-2] || raise).gsub(/('|\\")/, TRANS)}'"
12
- end
13
- end
data/lib/json_p3/token.rb DELETED
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "errors"
4
-
5
- module JSONP3
6
- # Tokens are produced by the lexer and consumed by the parser. Each token contains sub
7
- # string from a JSONPath expression, its location within the JSONPath expression and a
8
- # symbol indicating what type of token it is.
9
- class Token
10
- # @dynamic type, value, start, query, message
11
- attr_reader :type, :value, :start, :query, :message
12
-
13
- def initialize(type, value, start, query, message: nil)
14
- @type = type
15
- @value = value
16
- @start = start
17
- @query = query
18
- @message = message
19
- end
20
-
21
- def ==(other)
22
- self.class == other.class &&
23
- @type == other.type &&
24
- @value == other.value &&
25
- @start == other.start &&
26
- @query == other.query &&
27
- @message == other.message
28
- end
29
-
30
- alias eql? ==
31
-
32
- def hash
33
- [@type, @value].hash
34
- end
35
- end
36
- end
@@ -1,112 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JSONP3 # rubocop:disable Style/Documentation
4
- # Replace escape sequences with their equivalent Unicode code point.
5
- # @param value [String]
6
- # @param quote [String] one of '"' or "'".
7
- # @param token [Token]
8
- # @return [String] A new string without escape sequences.
9
- def self.unescape_string(value, quote, token)
10
- unescaped = String.new(encoding: "UTF-8")
11
- index = 0
12
- length = value.length
13
-
14
- while index < length
15
- ch = value[index] || raise
16
- if ch == "\\"
17
- index += 1
18
- case value[index]
19
- when quote
20
- unescaped << quote
21
- when "\\"
22
- unescaped << "\\"
23
- when "/"
24
- unescaped << "/"
25
- when "b"
26
- unescaped << "\x08"
27
- when "f"
28
- unescaped << "\x0C"
29
- when "n"
30
- unescaped << "\n"
31
- when "r"
32
- unescaped << "\r"
33
- when "t"
34
- unescaped << "\t"
35
- when "u"
36
- code_point, index = JSONP3.decode_hex_char(value, index, token)
37
- unescaped << JSONP3.code_point_to_string(code_point, token)
38
- else
39
- raise JSONPathSyntaxError.new("unknown escape sequence", token)
40
- end
41
- else
42
- raise JSONPathSyntaxError.new("invalid character", token) if ch.ord <= 0x1F
43
-
44
- unescaped << ch
45
- end
46
-
47
- index += 1
48
-
49
- end
50
-
51
- unescaped
52
- end
53
-
54
- def self.decode_hex_char(value, index, token)
55
- length = value.length
56
-
57
- raise JSONPathSyntaxError.new("incomplete escape sequence", token) if index + 4 >= length
58
-
59
- index += 1 # move past 'u'
60
- code_point = parse_hex_digits(value[index, 4], token)
61
-
62
- raise JSONPathSyntaxError.new("unexpected low surrogate", token) if low_surrogate?(code_point)
63
-
64
- return [code_point, index + 3] unless high_surrogate?(code_point)
65
-
66
- unless index + 9 < length && value[index + 4] == "\\" && value[index + 5] == "u"
67
- raise JSONPathSyntaxError.new("incomplete escape sequence", token)
68
- end
69
-
70
- low_surrogate = parse_hex_digits(value[index + 6, 10], token)
71
-
72
- raise JSONPathSyntaxError.new("unexpected low surrogate", token) unless low_surrogate?(low_surrogate)
73
-
74
- code_point = 0x10000 + (
75
- ((code_point & 0x03FF) << 10) | (low_surrogate & 0x03FF)
76
- )
77
-
78
- [code_point, index + 9]
79
- end
80
-
81
- def self.parse_hex_digits(digits, token)
82
- code_point = 0
83
- digits.each_byte do |b|
84
- code_point <<= 4
85
- case b
86
- when 48..57
87
- code_point |= b - 48
88
- when 65..70
89
- code_point |= b - 65 + 10
90
- when 97..102
91
- code_point |= b - 97 + 10
92
- else
93
- raise JSONPathSyntaxError.new("invalid escape sequence", token)
94
- end
95
- end
96
- code_point
97
- end
98
-
99
- def self.high_surrogate?(code_point)
100
- code_point >= 0xD800 && code_point <= 0xDBFF
101
- end
102
-
103
- def self.low_surrogate?(code_point)
104
- code_point >= 0xDC00 && code_point <= 0xDFFF
105
- end
106
-
107
- def self.code_point_to_string(code_point, token)
108
- raise JSONPathSyntaxError.new("invalid character", token) if code_point <= 0x1F
109
-
110
- code_point.chr(Encoding::UTF_8)
111
- end
112
- end