class NdrImport::Helpers::File::XmlStreaming::Cursor

Object to track state as the XML is iterated over, and detect when an element of interest is entered.

Constants

StackItem

wrapper to hold a representation of each element we descent into:

Public Class Methods

new(xpath, pattern_match_xpath) click to toggle source
# File lib/ndr_import/helpers/file/xml_streaming.rb, line 36
def initialize(xpath, pattern_match_xpath)
  @xpath = xpath
  @pattern_match_xpath = pattern_match_xpath
  @stack = []
  @match_depth = nil
end

Public Instance Methods

enter(node) click to toggle source
# File lib/ndr_import/helpers/file/xml_streaming.rb, line 52
def enter(node)
  @stack.push StackItem.new(node.name, node.attributes, node.empty_element?)
end
in?(node) click to toggle source

Has this cursor already passed inside a similar node? attribute comparison allows for e.g.: <SameName>

<SameName code="N"/>

</SameName>

# File lib/ndr_import/helpers/file/xml_streaming.rb, line 48
def in?(node)
  @stack.detect { |item| item.name == node.name && item.attrs == node.attributes }
end
inner_text() click to toggle source
# File lib/ndr_import/helpers/file/xml_streaming.rb, line 81
def inner_text
  dom_stubs[@stack].xpath(@xpath)&.inner_text
end
leave(_node) click to toggle source
# File lib/ndr_import/helpers/file/xml_streaming.rb, line 56
def leave(_node)
  @stack.pop
  @match_depth = nil if @match_depth && @stack.length < @match_depth
end
matches?() click to toggle source

Does the element that the cursor is currently on match what is being looked for?

# File lib/ndr_import/helpers/file/xml_streaming.rb, line 63
def matches?
  # Can't match again if we're inside a match already:
  return false if @matched_depth

  match = current_stack_match?

  # "empty element" matches are yielded immediately, without
  # tagging the stack as having matched, because there won't
  # be an equivalent closing tag to end the match with later.
  if in_empty_element?
    @stack.pop
  elsif match
    @match_depth = @stack.length
  end

  match
end

Private Instance Methods

add_items_to_dom(dom, items) click to toggle source

Helper to recursively build XML fragment.

# File lib/ndr_import/helpers/file/xml_streaming.rb, line 131
def add_items_to_dom(dom, items)
  item = items.shift
  dom.send(item.name, item.attrs) do
    add_items_to_dom(dom, items) if items.any?
  end
end
current_stack_match?() click to toggle source

Does the current state of the stack mean we’ve met the xpath criteria? Must be an exact match, not just matching a parent element in the DOM.

# File lib/ndr_import/helpers/file/xml_streaming.rb, line 94
def current_stack_match?
  parent_stack = @stack[0..-2]

  stack_match = if @pattern_match_xpath
                  dom_stubs[@stack].root.children.find_all do |node|
                    node.name =~ Regexp.new(@xpath)
                  end.first
                else
                  dom_stubs[@stack].at_xpath(@xpath)
                end

  return false unless stack_match

  parent_stack.empty? || xpath_not_in_parent_document?(dom_stubs[parent_stack])
end
dom_stubs() click to toggle source

A cached collection of DOM fragments, to represent the structure necessary to use xpath to descend into the main document’s DOM.

# File lib/ndr_import/helpers/file/xml_streaming.rb, line 122
def dom_stubs
  @dom_stubs ||= Hash.new do |hash, items|
    hash[items.dup] = Nokogiri::XML::Builder.new do |dom|
      add_items_to_dom(dom, items.dup)
    end.doc
  end
end
in_empty_element?() click to toggle source
# File lib/ndr_import/helpers/file/xml_streaming.rb, line 87
def in_empty_element?
  @stack.last.empty
end
xpath_not_in_parent_document?(parent_document) click to toggle source
# File lib/ndr_import/helpers/file/xml_streaming.rb, line 110
def xpath_not_in_parent_document?(parent_document)
  if @pattern_match_xpath
    parent_document.root.children.find_all do |node|
      node.name =~ Regexp.new(@xpath)
    end.first.nil?
  else
    !parent_document.at_xpath(@xpath)
  end
end