Class: Janeway::Query
- Inherits:
-
Object
- Object
- Janeway::Query
- Defined in:
- lib/janeway/query.rb
Overview
Query holds the abstract syntax tree created by parsing the query. This can be frozen and passed to multiple threads or ractors for simultaneous use. No instance members are modified during the interpretation stage.
Instance Attribute Summary collapse
-
#jsonpath ⇒ String
readonly
The original jsonpath query, for use in error messages.
- #root ⇒ AST::Root readonly
Instance Method Summary collapse
-
#==(other) ⇒ Object
Queries are considered equal if their ASTs evaluate to the same JSONPath string.
-
#dup ⇒ Query
Deep copy the query.
-
#enum_for(input) ⇒ Janeway::Enumerator
Combine this query with input to make an Enumerator.
-
#initialize(root_node, jsonpath) ⇒ Query
constructor
A new instance of Query.
-
#interpreter_tree_for(type) { ... } ⇒ Interpreters::Base
Return a cached interpreter tree for the given type, building it via the block on the first call.
-
#node_list ⇒ Array<Expression>
Return a list of the nodes in the AST.
-
#parent_query ⇒ Array(Query, AST::Selector)
Return a data-free split of this singular query into a "parent" Query plus the leaf selector — the parent addresses one level up from what
selfaddresses. -
#pop ⇒ AST::Selector
Delete the last element from the chain of selectors.
-
#singular_query? ⇒ Boolean
Return true if this query can only possibly match 0 or 1 elements in any input.
- #to_s ⇒ String
-
#tree ⇒ Object
Print AST in tree format Every AST class prints a 1-line representation of self, with children on separate lines.
Constructor Details
#initialize(root_node, jsonpath) ⇒ Query
Returns a new instance of Query.
17 18 19 20 21 22 23 |
# File 'lib/janeway/query.rb', line 17 def initialize(root_node, jsonpath) raise ArgumentError, "expect root identifier, got #{root_node.inspect}" unless root_node.is_a?(AST::RootNode) raise ArgumentError, "expect query string, got #{jsonpath.inspect}" unless jsonpath.is_a?(String) @root = root_node @jsonpath = jsonpath end |
Instance Attribute Details
#jsonpath ⇒ String (readonly)
The original jsonpath query, for use in error messages
10 11 12 |
# File 'lib/janeway/query.rb', line 10 def jsonpath @jsonpath end |
Instance Method Details
#==(other) ⇒ Object
Queries are considered equal if their ASTs evaluate to the same JSONPath string.
The string output is generated from the AST and should be considered a "normalized" form of the query. It may have different whitespace and parentheses than the original input but will be semantically equivalent.
87 88 89 |
# File 'lib/janeway/query.rb', line 87 def ==(other) to_s == other.to_s end |
#dup ⇒ Query
Deep copy the query.
Only the top-level linked list of segments/selectors is cloned — that is
the only part pop mutates. Nested contents (filter expressions,
function parameters, child-segment members) are shared with the original,
which is safe because the interpreter never mutates the AST.
The tree cache is intentionally NOT shared with the dup: cached trees reference the original's top-level chain and are invalid for a dup that may later be popped.
111 112 113 114 115 116 |
# File 'lib/janeway/query.rb', line 111 def dup cloned = node_list.map(&:dup) cloned.each_cons(2) { |a, b| a.next = b } cloned.last.next = nil Query.new(cloned.first, @jsonpath) end |
#enum_for(input) ⇒ Janeway::Enumerator
Combine this query with input to make an Enumerator. This can be used to iterate over results with #each, #map, etc.
29 30 31 |
# File 'lib/janeway/query.rb', line 29 def enum_for(input) Janeway::Enumerator.new(self, input) end |
#interpreter_tree_for(type) { ... } ⇒ Interpreters::Base
Return a cached interpreter tree for the given type, building it via
the block on the first call. Only stateless interpreter types
(:finder, :deleter) benefit from caching — :iterator and
:delete_if capture the caller's block, so their trees are
per-call.
No-op (yields fresh every time) if the Query is frozen — respecting the docstring's "can be frozen" contract.
58 59 60 61 62 63 64 |
# File 'lib/janeway/query.rb', line 58 def interpreter_tree_for(type) return yield unless %i[finder deleter].include?(type) return yield if frozen? @interpreter_tree_cache ||= {} @interpreter_tree_cache[type] ||= yield end |
#node_list ⇒ Array<Expression>
Return a list of the nodes in the AST. The AST of a jsonpath query is a straight line, so this is expressible as an array. The only part of the AST with branches is inside a filter selector, but that doesn't show up here.
70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/janeway/query.rb', line 70 def node_list nodes = [] node = @root loop do nodes << node break unless node.next node = node.next end nodes end |
#parent_query ⇒ Array(Query, AST::Selector)
Return a data-free split of this singular query into a "parent" Query
plus the leaf selector — the parent addresses one level up from what
self addresses. Used by Enumerator#insert to locate the container
that a new value should be inserted into.
147 148 149 150 151 |
# File 'lib/janeway/query.rb', line 147 def parent_query parent = dup leaf = parent.pop [parent, leaf] end |
#pop ⇒ AST::Selector
Delete the last element from the chain of selectors. For a singular query, this makes the query point to the match's parent instead of the match itself.
Don't do this for a non-singular query, those may contain child segments and descendant segments which would lead to different results.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/janeway/query.rb', line 125 def pop raise Janeway::Error.new('not allowed to pop from a non-singular query', to_s) unless singular_query? # Sever the link to the last selector nodes = node_list if nodes.size == 1 # Special case: cannot pop from the query "$" even though it is a singular query raise Janeway::Error.new('cannot pop from single-element query', to_s) end nodes[-2].next = nil # Return the last selector nodes.last end |
#singular_query? ⇒ Boolean
Return true if this query can only possibly match 0 or 1 elements in any input. Such a query must be composed exclusively of the root identifier followed by name selectors and / or index selectors.
42 43 44 |
# File 'lib/janeway/query.rb', line 42 def singular_query? @root.singular_query? end |
#to_s ⇒ String
34 35 36 |
# File 'lib/janeway/query.rb', line 34 def to_s @root.to_s end |
#tree ⇒ Object
Print AST in tree format Every AST class prints a 1-line representation of self, with children on separate lines
93 94 95 96 97 |
# File 'lib/janeway/query.rb', line 93 def tree result = @root.tree(0) result.flatten.join("\n") end |