janeway-jsonpath 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/janeway/ast/array_slice_selector.rb +6 -90
- data/lib/janeway/ast/binary_operator.rb +57 -51
- data/lib/janeway/ast/child_segment.rb +2 -5
- data/lib/janeway/ast/current_node.rb +2 -15
- data/lib/janeway/ast/descendant_segment.rb +0 -3
- data/lib/janeway/ast/expression.rb +34 -5
- data/lib/janeway/ast/function.rb +15 -10
- data/lib/janeway/ast/name_selector.rb +12 -7
- data/lib/janeway/ast/root_node.rb +2 -15
- data/lib/janeway/ast/selector.rb +5 -6
- data/lib/janeway/ast/unary_operator.rb +12 -2
- data/lib/janeway/ast/wildcard_selector.rb +2 -2
- data/lib/janeway/enumerator.rb +2 -4
- data/lib/janeway/error.rb +6 -1
- data/lib/janeway/functions/count.rb +1 -8
- data/lib/janeway/functions/length.rb +1 -12
- data/lib/janeway/functions/match.rb +3 -8
- data/lib/janeway/functions/search.rb +3 -8
- data/lib/janeway/functions/value.rb +1 -7
- data/lib/janeway/functions.rb +75 -2
- data/lib/janeway/interpreter.rb +50 -31
- data/lib/janeway/interpreters/array_slice_selector_delete_if.rb +10 -13
- data/lib/janeway/interpreters/array_slice_selector_interpreter.rb +56 -13
- data/lib/janeway/interpreters/binary_operator_interpreter.rb +18 -13
- data/lib/janeway/interpreters/descendant_segment_interpreter.rb +32 -13
- data/lib/janeway/interpreters/filter_selector_interpreter.rb +26 -34
- data/lib/janeway/interpreters/function_interpreter.rb +5 -1
- data/lib/janeway/interpreters/index_selector_delete_if.rb +0 -1
- data/lib/janeway/interpreters/iteration_helper.rb +19 -5
- data/lib/janeway/interpreters/root_node_delete_if.rb +57 -16
- data/lib/janeway/interpreters/tree_constructor.rb +0 -20
- data/lib/janeway/interpreters/wildcard_selector_delete_if.rb +16 -0
- data/lib/janeway/interpreters/yielder.rb +8 -2
- data/lib/janeway/lexer.rb +61 -78
- data/lib/janeway/location.rb +3 -1
- data/lib/janeway/normalized_path.rb +11 -2
- data/lib/janeway/parser.rb +46 -34
- data/lib/janeway/query.rb +47 -2
- data/lib/janeway/token.rb +2 -5
- data/lib/janeway/version.rb +1 -1
- metadata +2 -9
- data/lib/janeway/interpreters/array_slice_selector_deleter.rb +0 -41
- data/lib/janeway/interpreters/child_segment_deleter.rb +0 -19
- data/lib/janeway/interpreters/filter_selector_deleter.rb +0 -65
- data/lib/janeway/interpreters/index_selector_deleter.rb +0 -26
- data/lib/janeway/interpreters/name_selector_deleter.rb +0 -27
- data/lib/janeway/interpreters/root_node_deleter.rb +0 -34
- data/lib/janeway/interpreters/wildcard_selector_deleter.rb +0 -32
data/lib/janeway/query.rb
CHANGED
|
@@ -43,6 +43,26 @@ module Janeway
|
|
|
43
43
|
@root.singular_query?
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Return a cached interpreter tree for the given type, building it via
|
|
47
|
+
# the block on the first call. Only stateless interpreter types
|
|
48
|
+
# (`:finder`, `:deleter`) benefit from caching — `:iterator` and
|
|
49
|
+
# `:delete_if` capture the caller's block, so their trees are
|
|
50
|
+
# per-call.
|
|
51
|
+
#
|
|
52
|
+
# No-op (yields fresh every time) if the Query is frozen — respecting
|
|
53
|
+
# the docstring's "can be frozen" contract.
|
|
54
|
+
#
|
|
55
|
+
# @param type [Symbol] :finder, :deleter, :iterator, :delete_if
|
|
56
|
+
# @yield build the tree if not cached
|
|
57
|
+
# @return [Interpreters::Base]
|
|
58
|
+
def interpreter_tree_for(type)
|
|
59
|
+
return yield unless %i[finder deleter].include?(type)
|
|
60
|
+
return yield if frozen?
|
|
61
|
+
|
|
62
|
+
@interpreter_tree_cache ||= {}
|
|
63
|
+
@interpreter_tree_cache[type] ||= yield
|
|
64
|
+
end
|
|
65
|
+
|
|
46
66
|
# Return a list of the nodes in the AST.
|
|
47
67
|
# The AST of a jsonpath query is a straight line, so this is expressible as an array.
|
|
48
68
|
# The only part of the AST with branches is inside a filter selector, but that doesn't show up here.
|
|
@@ -76,10 +96,23 @@ module Janeway
|
|
|
76
96
|
result.flatten.join("\n")
|
|
77
97
|
end
|
|
78
98
|
|
|
79
|
-
# Deep copy the query
|
|
99
|
+
# Deep copy the query.
|
|
100
|
+
#
|
|
101
|
+
# Only the top-level linked list of segments/selectors is cloned — that is
|
|
102
|
+
# the only part `pop` mutates. Nested contents (filter expressions,
|
|
103
|
+
# function parameters, child-segment members) are shared with the original,
|
|
104
|
+
# which is safe because the interpreter never mutates the AST.
|
|
105
|
+
#
|
|
106
|
+
# The tree cache is intentionally NOT shared with the dup: cached trees
|
|
107
|
+
# reference the original's top-level chain and are invalid for a dup that
|
|
108
|
+
# may later be popped.
|
|
109
|
+
#
|
|
80
110
|
# @return [Query]
|
|
81
111
|
def dup
|
|
82
|
-
|
|
112
|
+
cloned = node_list.map(&:dup)
|
|
113
|
+
cloned.each_cons(2) { |a, b| a.next = b }
|
|
114
|
+
cloned.last.next = nil
|
|
115
|
+
Query.new(cloned.first, @jsonpath)
|
|
83
116
|
end
|
|
84
117
|
|
|
85
118
|
# Delete the last element from the chain of selectors.
|
|
@@ -104,5 +137,17 @@ module Janeway
|
|
|
104
137
|
# Return the last selector
|
|
105
138
|
nodes.last
|
|
106
139
|
end
|
|
140
|
+
|
|
141
|
+
# Return a data-free split of this singular query into a "parent" Query
|
|
142
|
+
# plus the leaf selector — the parent addresses one level up from what
|
|
143
|
+
# `self` addresses. Used by Enumerator#insert to locate the container
|
|
144
|
+
# that a new value should be inserted into.
|
|
145
|
+
#
|
|
146
|
+
# @return [Array(Query, AST::Selector)] [parent_query, leaf_selector]
|
|
147
|
+
def parent_query
|
|
148
|
+
parent = dup
|
|
149
|
+
leaf = parent.pop
|
|
150
|
+
[parent, leaf]
|
|
151
|
+
end
|
|
107
152
|
end
|
|
108
153
|
end
|
data/lib/janeway/token.rb
CHANGED
|
@@ -7,10 +7,7 @@ module Janeway
|
|
|
7
7
|
class Token
|
|
8
8
|
extend Forwardable
|
|
9
9
|
|
|
10
|
-
attr_reader :type, :lexeme, :location
|
|
11
|
-
|
|
12
|
-
# write-access so '-' operator can modify number value
|
|
13
|
-
attr_accessor :literal
|
|
10
|
+
attr_reader :type, :lexeme, :literal, :location
|
|
14
11
|
|
|
15
12
|
def_delegators :@location, :line, :col, :length
|
|
16
13
|
|
|
@@ -31,7 +28,7 @@ module Janeway
|
|
|
31
28
|
when Integer, String then @literal == other
|
|
32
29
|
when Symbol then @type == other
|
|
33
30
|
when Token
|
|
34
|
-
@type == other.type && @lexeme == other.
|
|
31
|
+
@type == other.type && @lexeme == other.lexeme && @literal == other.literal
|
|
35
32
|
else
|
|
36
33
|
raise ArgumentError, "don't know how to compare Token with #{other.inspect}"
|
|
37
34
|
end
|
data/lib/janeway/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: janeway-jsonpath
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Fraser Hanson
|
|
@@ -62,33 +62,26 @@ files:
|
|
|
62
62
|
- lib/janeway/functions/value.rb
|
|
63
63
|
- lib/janeway/interpreter.rb
|
|
64
64
|
- lib/janeway/interpreters/array_slice_selector_delete_if.rb
|
|
65
|
-
- lib/janeway/interpreters/array_slice_selector_deleter.rb
|
|
66
65
|
- lib/janeway/interpreters/array_slice_selector_interpreter.rb
|
|
67
66
|
- lib/janeway/interpreters/base.rb
|
|
68
67
|
- lib/janeway/interpreters/binary_operator_interpreter.rb
|
|
69
68
|
- lib/janeway/interpreters/child_segment_delete_if.rb
|
|
70
|
-
- lib/janeway/interpreters/child_segment_deleter.rb
|
|
71
69
|
- lib/janeway/interpreters/child_segment_interpreter.rb
|
|
72
70
|
- lib/janeway/interpreters/current_node_interpreter.rb
|
|
73
71
|
- lib/janeway/interpreters/descendant_segment_interpreter.rb
|
|
74
72
|
- lib/janeway/interpreters/filter_selector_delete_if.rb
|
|
75
|
-
- lib/janeway/interpreters/filter_selector_deleter.rb
|
|
76
73
|
- lib/janeway/interpreters/filter_selector_interpreter.rb
|
|
77
74
|
- lib/janeway/interpreters/function_interpreter.rb
|
|
78
75
|
- lib/janeway/interpreters/index_selector_delete_if.rb
|
|
79
|
-
- lib/janeway/interpreters/index_selector_deleter.rb
|
|
80
76
|
- lib/janeway/interpreters/index_selector_interpreter.rb
|
|
81
77
|
- lib/janeway/interpreters/iteration_helper.rb
|
|
82
78
|
- lib/janeway/interpreters/name_selector_delete_if.rb
|
|
83
|
-
- lib/janeway/interpreters/name_selector_deleter.rb
|
|
84
79
|
- lib/janeway/interpreters/name_selector_interpreter.rb
|
|
85
80
|
- lib/janeway/interpreters/root_node_delete_if.rb
|
|
86
|
-
- lib/janeway/interpreters/root_node_deleter.rb
|
|
87
81
|
- lib/janeway/interpreters/root_node_interpreter.rb
|
|
88
82
|
- lib/janeway/interpreters/tree_constructor.rb
|
|
89
83
|
- lib/janeway/interpreters/unary_operator_interpreter.rb
|
|
90
84
|
- lib/janeway/interpreters/wildcard_selector_delete_if.rb
|
|
91
|
-
- lib/janeway/interpreters/wildcard_selector_deleter.rb
|
|
92
85
|
- lib/janeway/interpreters/wildcard_selector_interpreter.rb
|
|
93
86
|
- lib/janeway/interpreters/yielder.rb
|
|
94
87
|
- lib/janeway/lexer.rb
|
|
@@ -116,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
116
109
|
- !ruby/object:Gem::Version
|
|
117
110
|
version: '0'
|
|
118
111
|
requirements: []
|
|
119
|
-
rubygems_version:
|
|
112
|
+
rubygems_version: 4.0.8
|
|
120
113
|
specification_version: 4
|
|
121
114
|
summary: jsonpath parser which implements the finalized IETF standard of Goessner
|
|
122
115
|
JSONPath
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'array_slice_selector_interpreter'
|
|
4
|
-
|
|
5
|
-
module Janeway
|
|
6
|
-
module Interpreters
|
|
7
|
-
# Interprets array slice selector and deletes matching values
|
|
8
|
-
class ArraySliceSelectorDeleter < ArraySliceSelectorInterpreter
|
|
9
|
-
# Delete values matched by the array slice selector
|
|
10
|
-
#
|
|
11
|
-
# @param input [Array, Hash] the results of processing so far
|
|
12
|
-
# @param _parent [Array, Hash] parent of the input object
|
|
13
|
-
# @param _root [Array, Hash] the entire input
|
|
14
|
-
# @param _path [Array<String>] elements of normalized path to the current input
|
|
15
|
-
# @return [Array]
|
|
16
|
-
def interpret(input, _parent, _root, _path)
|
|
17
|
-
return [] unless input.is_a?(Array)
|
|
18
|
-
return [] if selector&.step&.zero? # RFC: When step is 0, no elements are selected.
|
|
19
|
-
|
|
20
|
-
# Calculate the upper and lower indices of the target range
|
|
21
|
-
lower = selector.lower_index(input.size)
|
|
22
|
-
upper = selector.upper_index(input.size)
|
|
23
|
-
|
|
24
|
-
# Convert bounds and step to index values.
|
|
25
|
-
# Omit the final index, since no value is collected for that.
|
|
26
|
-
# Delete indexes from largest to smallest, so that deleting an index does
|
|
27
|
-
# not change the remaining indexes
|
|
28
|
-
results = []
|
|
29
|
-
if selector.step.positive?
|
|
30
|
-
indexes = lower.step(to: upper - 1, by: selector.step).to_a
|
|
31
|
-
indexes.reverse_each { |i| results << input.delete_at(i) }
|
|
32
|
-
results.reverse
|
|
33
|
-
else
|
|
34
|
-
indexes = upper.step(to: lower + 1, by: selector.step).to_a
|
|
35
|
-
indexes.each { |i| results << input.delete_at(i) }
|
|
36
|
-
results
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'child_segment_interpreter'
|
|
4
|
-
|
|
5
|
-
module Janeway
|
|
6
|
-
module Interpreters
|
|
7
|
-
# Child segment interpreter with selectors that delete matching elements
|
|
8
|
-
class ChildSegmentDeleter < ChildSegmentInterpreter
|
|
9
|
-
# @param child_segment [AST::ChildSegment]
|
|
10
|
-
def initialize(child_segment)
|
|
11
|
-
super
|
|
12
|
-
@selectors =
|
|
13
|
-
child_segment.map do |expr|
|
|
14
|
-
TreeConstructor.ast_node_to_deleter(expr)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'filter_selector_interpreter'
|
|
4
|
-
|
|
5
|
-
module Janeway
|
|
6
|
-
module Interpreters
|
|
7
|
-
# Interprets a filter selector, and deletes matching values
|
|
8
|
-
class FilterSelectorDeleter < FilterSelectorInterpreter
|
|
9
|
-
# Interpret selector on the input.
|
|
10
|
-
# @param input [Hash] the results of processing so far
|
|
11
|
-
# @param root [Array, Hash] the entire input
|
|
12
|
-
# @param _path [Array<String>] elements of normalized path to the current input
|
|
13
|
-
def interpret_hash(input, root, _path)
|
|
14
|
-
# Apply filter expressions to the input data
|
|
15
|
-
results = []
|
|
16
|
-
input.each do |key, value|
|
|
17
|
-
# Run filter and interpret result
|
|
18
|
-
result = @expr.interpret(value, nil, root, [])
|
|
19
|
-
case result
|
|
20
|
-
when TrueClass then results << value # comparison test - pass
|
|
21
|
-
when FalseClass then next # comparison test - fail
|
|
22
|
-
when Array
|
|
23
|
-
next if result.empty?
|
|
24
|
-
|
|
25
|
-
results << value # existence test - node list
|
|
26
|
-
else
|
|
27
|
-
results << value # existence test. Null values here == success.
|
|
28
|
-
end
|
|
29
|
-
input.delete(key)
|
|
30
|
-
end
|
|
31
|
-
results
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Interpret selector on the input.
|
|
35
|
-
# @param input [Array] the results of processing so far
|
|
36
|
-
# @param root [Array, Hash] the entire input
|
|
37
|
-
# @param _path [Array<String>] elements of normalized path to the current input
|
|
38
|
-
def interpret_array(input, root, _path)
|
|
39
|
-
# Apply filter expressions to the input data
|
|
40
|
-
results = []
|
|
41
|
-
|
|
42
|
-
# Iterate in reverse order so that deletion does not alter the remaining indexes
|
|
43
|
-
i = input.size
|
|
44
|
-
input.reverse_each do |value|
|
|
45
|
-
i -= 1 # calculate array index
|
|
46
|
-
|
|
47
|
-
# Run filter and interpret result
|
|
48
|
-
result = @expr.interpret(value, nil, root, [])
|
|
49
|
-
case result
|
|
50
|
-
when TrueClass then results << value # comparison test - pass
|
|
51
|
-
when FalseClass then next # comparison test - fail
|
|
52
|
-
when Array
|
|
53
|
-
next if result.empty?
|
|
54
|
-
|
|
55
|
-
results << value # existence test - node list
|
|
56
|
-
else
|
|
57
|
-
results << value # existence test. Null values here == success.
|
|
58
|
-
end
|
|
59
|
-
input.delete_at(i)
|
|
60
|
-
end
|
|
61
|
-
results.reverse
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'index_selector_interpreter'
|
|
4
|
-
|
|
5
|
-
module Janeway
|
|
6
|
-
module Interpreters
|
|
7
|
-
# Interprets an index selector, and deletes the matched value.
|
|
8
|
-
class IndexSelectorDeleter < IndexSelectorInterpreter
|
|
9
|
-
# Interpret selector on the given input.
|
|
10
|
-
# @param input [Array, Hash] the results of processing so far
|
|
11
|
-
# @param _parent [Array, Hash] parent of the input object
|
|
12
|
-
# @param _root [Array, Hash] the entire input
|
|
13
|
-
# @param _path [Array<String>] elements of normalized path to the current input
|
|
14
|
-
def interpret(input, _parent, _root, _path)
|
|
15
|
-
return [] unless input.is_a?(Array)
|
|
16
|
-
|
|
17
|
-
index = selector.value
|
|
18
|
-
result = input.fetch(index) # raises IndexError if no such index
|
|
19
|
-
input.delete_at(index) # returns nil if deleted value is nil, or if no value was deleted
|
|
20
|
-
[result]
|
|
21
|
-
rescue IndexError
|
|
22
|
-
[]
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'name_selector_interpreter'
|
|
4
|
-
|
|
5
|
-
module Janeway
|
|
6
|
-
module Interpreters
|
|
7
|
-
# Interprets a name selector, and deletes the matched values.
|
|
8
|
-
class NameSelectorDeleter < NameSelectorInterpreter
|
|
9
|
-
# Interpret selector on the given input.
|
|
10
|
-
# @param input [Array, Hash] the results of processing so far
|
|
11
|
-
# @param _parent [Array, Hash] parent of the input object
|
|
12
|
-
# @param _root [Array, Hash] the entire input
|
|
13
|
-
# @param _path [Array<String>] elements of normalized path to the current input
|
|
14
|
-
def interpret(input, _parent, _root, _path)
|
|
15
|
-
return [] unless input.is_a?(Hash) && input.key?(name)
|
|
16
|
-
|
|
17
|
-
[input.delete(name)]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Return hash representation of this interpreter
|
|
21
|
-
# @return [Hash]
|
|
22
|
-
def as_json
|
|
23
|
-
{ type: type, value: name, next: @next&.as_json }
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'root_node_interpreter'
|
|
4
|
-
|
|
5
|
-
module Janeway
|
|
6
|
-
module Interpreters
|
|
7
|
-
# Interprets the root node for deletion
|
|
8
|
-
class RootNodeDeleter < RootNodeInterpreter
|
|
9
|
-
# Delete all values from the root node.
|
|
10
|
-
#
|
|
11
|
-
# TODO: unclear, for deletion is there a difference between queries '$' and '$.*'?
|
|
12
|
-
#
|
|
13
|
-
# @param _input [Array, Hash] the results of processing so far
|
|
14
|
-
# @param _parent [Array, Hash] parent of the input object
|
|
15
|
-
# @param root [Array, Hash] the entire input
|
|
16
|
-
# @param _path [Array<String>] elements of normalized path to the current input
|
|
17
|
-
# @return [Array] deleted elements
|
|
18
|
-
def interpret(_input, _parent, root, _path)
|
|
19
|
-
case root
|
|
20
|
-
when Array
|
|
21
|
-
results = root.dup
|
|
22
|
-
root.clear
|
|
23
|
-
results
|
|
24
|
-
when Hash
|
|
25
|
-
results = root.values
|
|
26
|
-
root.clear
|
|
27
|
-
results
|
|
28
|
-
else
|
|
29
|
-
[]
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'wildcard_selector_interpreter'
|
|
4
|
-
|
|
5
|
-
module Janeway
|
|
6
|
-
module Interpreters
|
|
7
|
-
# Interprets a wildcard selector, and deletes the results.
|
|
8
|
-
class WildcardSelectorDeleter < WildcardSelectorInterpreter
|
|
9
|
-
# Delete all elements from the input
|
|
10
|
-
#
|
|
11
|
-
# @param input [Array, Hash] the results of processing so far
|
|
12
|
-
# @param _parent [Array, Hash] parent of the input object
|
|
13
|
-
# @param _root [Array, Hash] the entire input
|
|
14
|
-
# @param _path [Array<String>] elements of normalized path to the current input
|
|
15
|
-
# @return [Array] deleted elements
|
|
16
|
-
def interpret(input, _parent, _root, _path)
|
|
17
|
-
case input
|
|
18
|
-
when Array
|
|
19
|
-
results = input.dup
|
|
20
|
-
input.clear
|
|
21
|
-
results
|
|
22
|
-
when Hash
|
|
23
|
-
results = input.values
|
|
24
|
-
input.clear
|
|
25
|
-
results
|
|
26
|
-
else
|
|
27
|
-
[]
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|