janeway-jsonpath 0.6.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/README.md +1 -1
- 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 +3 -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 +35 -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 +14 -7
- 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 -35
- 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 +1 -0
- data/lib/janeway/interpreters/index_selector_interpreter.rb +3 -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 +66 -77
- data/lib/janeway/location.rb +3 -1
- data/lib/janeway/normalized_path.rb +23 -7
- data/lib/janeway/parser.rb +46 -34
- data/lib/janeway/query.rb +48 -5
- data/lib/janeway/token.rb +2 -5
- data/lib/janeway/version.rb +1 -1
- data/lib/janeway.rb +16 -3
- metadata +3 -10
- 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/parser.rb
CHANGED
|
@@ -16,6 +16,32 @@ module Janeway
|
|
|
16
16
|
UNARY_OPERATORS = %w[! -].freeze
|
|
17
17
|
BINARY_OPERATORS = %w[== != > < >= <= ,].freeze
|
|
18
18
|
LOGICAL_OPERATORS = %w[&& ||].freeze
|
|
19
|
+
|
|
20
|
+
# O(1)-lookup Hash companions to the arrays above, plus the union used by
|
|
21
|
+
# determine_infix_function. Building these inline in the hot path
|
|
22
|
+
# allocated a fresh Array per token; hoisting eliminates it.
|
|
23
|
+
UNARY_OPERATOR_SET = UNARY_OPERATORS.each_with_object({}) { |op, h| h[op] = true }.freeze
|
|
24
|
+
BINARY_OPERATOR_SET = BINARY_OPERATORS.each_with_object({}) { |op, h| h[op] = true }.freeze
|
|
25
|
+
INFIX_OPERATOR_SET =
|
|
26
|
+
(BINARY_OPERATORS + LOGICAL_OPERATORS).each_with_object({}) { |op, h| h[op] = true }.freeze
|
|
27
|
+
PARSE_METHOD_TYPES =
|
|
28
|
+
%I[identifier number string true false nil function if while root current_node]
|
|
29
|
+
.each_with_object({}) { |t, h| h[t] = true }.freeze
|
|
30
|
+
TERMINATOR_TYPES = %I[child_end union eof].each_with_object({}) { |t, h| h[t] = true }.freeze
|
|
31
|
+
|
|
32
|
+
# O(1) dispatch table for determine_parsing_function's type-driven cases.
|
|
33
|
+
# Sits next to the PARSE_METHOD_TYPES set (the other type branch) so both
|
|
34
|
+
# are visible when adding a new token type.
|
|
35
|
+
PARSE_DISPATCH_BY_TYPE = {
|
|
36
|
+
group_start: :parse_grouped_expr,
|
|
37
|
+
:"\n" => :parse_terminator,
|
|
38
|
+
eof: :parse_terminator,
|
|
39
|
+
child_start: :parse_child_segment,
|
|
40
|
+
dot: :parse_dot_notation,
|
|
41
|
+
descendants: :parse_descendant_segment,
|
|
42
|
+
filter: :parse_filter_selector,
|
|
43
|
+
null: :parse_null,
|
|
44
|
+
}.freeze
|
|
19
45
|
LOWEST_PRECEDENCE = 0
|
|
20
46
|
OPERATOR_PRECEDENCE = {
|
|
21
47
|
',' => 0,
|
|
@@ -65,12 +91,8 @@ module Janeway
|
|
|
65
91
|
|
|
66
92
|
private
|
|
67
93
|
|
|
68
|
-
def pending_tokens?
|
|
69
|
-
@next_p < tokens.length
|
|
70
|
-
end
|
|
71
|
-
|
|
72
94
|
def next_not_terminator?
|
|
73
|
-
next_token && next_token.type != :
|
|
95
|
+
next_token && next_token.type != :eof
|
|
74
96
|
end
|
|
75
97
|
|
|
76
98
|
# Make "next" token become "current" by moving the pointer
|
|
@@ -135,33 +157,21 @@ module Janeway
|
|
|
135
157
|
end
|
|
136
158
|
|
|
137
159
|
def determine_parsing_function
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
:parse_child_segment
|
|
149
|
-
elsif current.type == :dot # .
|
|
150
|
-
:parse_dot_notation
|
|
151
|
-
elsif current.type == :descendants # ..
|
|
152
|
-
:parse_descendant_segment
|
|
153
|
-
elsif current.type == :filter # ?
|
|
154
|
-
:parse_filter_selector
|
|
155
|
-
elsif current.type == :null # null
|
|
156
|
-
:parse_null
|
|
157
|
-
else
|
|
158
|
-
raise err("Don't know how to parse #{current}")
|
|
159
|
-
end
|
|
160
|
+
# Two type-driven tables plus a lexeme-driven check for unary operators.
|
|
161
|
+
# PARSE_METHOD_TYPES synthesizes the method name (`:parse_<type>`);
|
|
162
|
+
# PARSE_DISPATCH_BY_TYPE maps to a fixed method name.
|
|
163
|
+
return :"parse_#{current.type}" if PARSE_METHOD_TYPES.include?(current.type)
|
|
164
|
+
|
|
165
|
+
dispatch = PARSE_DISPATCH_BY_TYPE[current.type]
|
|
166
|
+
return dispatch if dispatch
|
|
167
|
+
return :parse_unary_operator if UNARY_OPERATOR_SET.include?(current.lexeme)
|
|
168
|
+
|
|
169
|
+
raise err("Don't know how to parse #{current}")
|
|
160
170
|
end
|
|
161
171
|
|
|
162
172
|
# @return [nil, Symbol]
|
|
163
173
|
def determine_infix_function(token = current)
|
|
164
|
-
return unless
|
|
174
|
+
return unless INFIX_OPERATOR_SET.include?(token.lexeme)
|
|
165
175
|
|
|
166
176
|
:parse_binary_operator
|
|
167
177
|
end
|
|
@@ -198,14 +208,16 @@ module Janeway
|
|
|
198
208
|
end
|
|
199
209
|
|
|
200
210
|
# '-' must be followed by a number token.
|
|
201
|
-
#
|
|
211
|
+
# Consume the minus, then replace the following number token in the
|
|
212
|
+
# token stream with a copy that has the negated literal. Replacing —
|
|
213
|
+
# rather than mutating in place — keeps Token immutable.
|
|
202
214
|
consume
|
|
203
|
-
parse_number
|
|
204
215
|
unless current.literal.is_a?(Numeric)
|
|
205
216
|
raise err("Minus operator \"-\" must be followed by number, got #{current.lexeme.inspect}")
|
|
206
217
|
end
|
|
207
218
|
|
|
208
|
-
|
|
219
|
+
original = current
|
|
220
|
+
@tokens[@next_p - 1] = Token.new(original.type, original.lexeme, -original.literal, original.location)
|
|
209
221
|
current
|
|
210
222
|
end
|
|
211
223
|
|
|
@@ -481,11 +493,10 @@ module Janeway
|
|
|
481
493
|
# Feed tokens to the FilterSelector until hitting a terminator
|
|
482
494
|
def parse_filter_selector
|
|
483
495
|
selector = AST::FilterSelector.new
|
|
484
|
-
|
|
485
|
-
while next_token && !terminator_types.include?(next_token.type)
|
|
496
|
+
while next_token && !TERMINATOR_TYPES.include?(next_token.type)
|
|
486
497
|
consume
|
|
487
498
|
node =
|
|
488
|
-
if
|
|
499
|
+
if BINARY_OPERATOR_SET.include?(current.lexeme)
|
|
489
500
|
parse_binary_operator(selector.value)
|
|
490
501
|
else
|
|
491
502
|
parse_expr_recursively
|
|
@@ -529,6 +540,7 @@ module Janeway
|
|
|
529
540
|
|
|
530
541
|
consume
|
|
531
542
|
op.right = parse_expr_recursively(op_precedence)
|
|
543
|
+
op.validate!
|
|
532
544
|
|
|
533
545
|
op
|
|
534
546
|
end
|
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.
|
|
@@ -90,9 +123,7 @@ module Janeway
|
|
|
90
123
|
#
|
|
91
124
|
# @return [AST::Selector]
|
|
92
125
|
def pop
|
|
93
|
-
unless singular_query?
|
|
94
|
-
raise Janeway::Error.new('not allowed to pop from a non-singular query', to_s)
|
|
95
|
-
end
|
|
126
|
+
raise Janeway::Error.new('not allowed to pop from a non-singular query', to_s) unless singular_query?
|
|
96
127
|
|
|
97
128
|
# Sever the link to the last selector
|
|
98
129
|
nodes = node_list
|
|
@@ -106,5 +137,17 @@ module Janeway
|
|
|
106
137
|
# Return the last selector
|
|
107
138
|
nodes.last
|
|
108
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
|
|
109
152
|
end
|
|
110
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
data/lib/janeway.rb
CHANGED
|
@@ -54,8 +54,8 @@ module Janeway
|
|
|
54
54
|
Janeway::Parser.parse(query)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
# Transform a jsonpath singular query into an array of
|
|
58
|
-
# providing to Hash#dig or Array#dig.
|
|
57
|
+
# Transform a jsonpath singular query into an array of hash keys and/or array
|
|
58
|
+
# indexes suitable for providing to Hash#dig or Array#dig.
|
|
59
59
|
#
|
|
60
60
|
# Only singular queries are allowed, meaning queries that contain only name
|
|
61
61
|
# selectors (ie. hash keys) and index selectors (array indexes.)
|
|
@@ -67,6 +67,19 @@ module Janeway
|
|
|
67
67
|
# @param jsonpath [String] jsonpath query
|
|
68
68
|
# @return [Array<String, Integer>]
|
|
69
69
|
def self.path_to_diggable(jsonpath)
|
|
70
|
-
raise
|
|
70
|
+
raise Janeway::Error.new('Query has nothing to dig', jsonpath) if jsonpath == '$'
|
|
71
|
+
|
|
72
|
+
# Parse query and determine whether it can be converted
|
|
73
|
+
query = parse(jsonpath)
|
|
74
|
+
unless query.singular_query?
|
|
75
|
+
raise Janeway::Error.new('Only a singular query can be converted to dig parameters', jsonpath)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Convert query to a list of name and index selectors
|
|
79
|
+
nodes = query.node_list
|
|
80
|
+
nodes.shift # discard the root identifier
|
|
81
|
+
|
|
82
|
+
# Extract values from selectors
|
|
83
|
+
nodes.map(&:value)
|
|
71
84
|
end
|
|
72
85
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: janeway-jsonpath
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Fraser Hanson
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: |+
|
|
13
13
|
JSONPath is a query language for selecting and extracting values from a JSON text.
|
|
@@ -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
|