class Arboretum::DocTree::Elements::Element

Generic element class Meant to be inherited rather than used directly All elements hold reference to a parent, children, and their left and right siblings

Attributes

break_within[RW]
children[RW]
history[RW]
incrementers[RW]
library[RW]
parent[RW]
resetters[RW]
sibling_next[RW]
sibling_prev[RW]
tree_residence[R]

Public Class Methods

create(namespace: nil, tag: nil, attrs: {}, text: nil) { |created_element| ... } click to toggle source

Create a custom element with a given namespace, tag, attributes, and text child If only text is given, a TextElement will be created rather than a tagged element If no tag is given, but attributes or namespace are given, a `div` element will be used by default

# File lib/arboretum/doctree.rb, line 464
def self.create(namespace: nil, tag: nil, attrs: {}, text: nil)
  tag = :div if tag.nil? and (!namespace.nil? or !attrs.empty? or text.nil?)
  created_element = nil
  if tag.nil?
    raise TypeError.new("Text must be a String or TextElement") if !(text.is_a?(String) or text.is_a?(TextElement))
    created_element = (text.is_a?(String)) ? TextElement.new(text) : text
  else
    tag = tag.to_sym if !tag.is_a?(Symbol)
    raise TypeError.new("Tag must be a Symbol or String") if !tag.is_a?(Symbol)
    namespace = namespace.to_sym if namespace.kind_of?(String) and !namespace.nil?
    raise TypeError.new("Namespace must be a Symbol or String") if !namespace.is_a?(Symbol) and !namespace.nil?
    sanitary_attrs = {}
    attrs.each do |key, val|
      key = key.to_sym if !key.is_a?(Symbol)
      raise TypeError.new("Attribute name must be a Symbol or String") if !key.is_a?(Symbol)
      val = val.split if val.is_a?(String)
      # Ensure value is arrays of strings
      raise TypeError.new("Attribute value must be a String or Array of Strings") if !val.is_a?(Array)
      val.each{|sub_val| raise TypeError.new("Each attribute value in an array must be a String") if !sub_val.is_a?(String)}
      sanitary_attrs[key] = val
    end
    created_element = TaggedElement.new(namespace, tag, sanitary_attrs)
    if !text.nil?
      raise TypeError.new("Text must be a String or TextElement") if !(text.is_a?(String) or text.is_a?(TextElement))
      if text.is_a?(String)
        text_child = TextElement.new(text)
        created_element.append_child(text_child)
      else
        created_element.append_child(text)
      end
    end
  end
  created_element.break_within = true if !created_element.is_a?(TextElement)
  yield created_element if block_given?
  created_element
end
new() click to toggle source
# File lib/arboretum/doctree.rb, line 503
def initialize
  # Family of elements
  @parent = nil           # Element
  @sibling_prev = nil     # Element
  @sibling_next = nil     # Element
  @children = []          # Array of Elements

  # Tree residence
  @tree_residence = nil   # Tree

  # Properties, references, and counters
  @break_within = false     # Boolean
  @incrementers = Hash.new  # Hash with key: Symbol (name), value: Incrementer
  @resetters = Hash.new     # Hash with key: Symbol (name), value: Resetter
  @library = Hash.new       # Hash with key: Symbol, value: Element/ElementGroup
  @history = Array.new      # Array of arrays of form [method_called, arguments, callers]
end
stitch!(prev_element, next_element) click to toggle source

Class method to stitch together two elements as siblings, ordered Will stitch values even if one or both is nil When used in isolation, can cause a mismatch in the tree (because parent is not updated)

# File lib/arboretum/doctree.rb, line 456
def self.stitch!(prev_element, next_element)
  prev_element.set_sibling_next!(next_element) unless prev_element.nil?
  next_element.set_sibling_prev!(prev_element) unless next_element.nil?
end

Public Instance Methods

<<(*elements)
Alias for: append_child
[](category)
Alias for: lib_get
allude(other, category)
Alias for: lib_add
ancestors() { |parent| ... } click to toggle source

Get list of all this element's ancesotrs in depth-first order

# File lib/arboretum/doctree.rb, line 853
def ancestors
  parent_list = []
  current = self
  while not current.parent.nil?
    parent_list << current.parent
    current = current.parent
  end
  parent_list = parent_list.reverse
  parent_list.each {|parent| yield parent if block_given?}
  ElementGroup.new(parent_list)
end
ancestors_reverse() { |parent| ... } click to toggle source

Get list of all of this element's ancestors in reversed depth-first order Used for slightly speedier and practical searching

# File lib/arboretum/doctree.rb, line 867
def ancestors_reverse
  parent_list = []
  current = self
  while not current.parent.nil?
    yield current.parent if block_given?
    parent_list << current.parent
    current = current.parent
  end
  ElementGroup.new(parent_list)
end
append_child(*elements) { |member| ... } click to toggle source

Add an element as this element's last child

# File lib/arboretum/doctree.rb, line 522
def append_child(*elements)
  placed = Array.new
  elements.each do |element|
    if !element.nil?
      element = TextElement.new(element) if element.is_a?(String)
      element = Element.create(element) if element.is_a?(Hash)
      element.graft_last_onto(self)
      element.listing.each do |member|
        yield member if block_given?
        placed << member
      end
    end
  end
  return nil if placed.empty?
  return placed.first if placed.length == 1
  return ElementGroup.new(placed)
end
Also aliased as: push, <<
append_sibling(*elements) { |member| ... } click to toggle source
# File lib/arboretum/doctree.rb, line 582
def append_sibling(*elements)
  placed = Array.new
  elements.each do |element|
    if !element.nil?
      element = TextElement.new(element) if element.is_a?(String)
      element = Element.create(element) if element.is_a?(Hash)
      element.graft_onto(self.parent, self.index_in_parent+1)
      element.listing.each do |member|
        yield member if block_given?
        placed << member
      end
    end
  end
  return nil if placed.empty?
  return placed.first if placed.length == 1
  return ElementGroup.new(placed)
end
can_have_children?() click to toggle source
# File lib/arboretum/doctree.rb, line 930
def can_have_children?
  false
end
cite(other, category)
Alias for: lib_add
clear()
Alias for: detach
content() click to toggle source

Special method to get an elements children as an AdjacentElementGroup

# File lib/arboretum/doctree.rb, line 660
def content
  group = AdjacentElementGroup.new
  group.base(@children[0]) if not @children.empty?
  group.fill
end
Also aliased as: contents
contents()
Alias for: content
copy() click to toggle source

General element deep copy method

# File lib/arboretum/doctree.rb, line 717
def copy
  element_copy = Element.new
  element_copy.set_children!(@children.map {|child| child.copy})
  element_copy
end
count(counter_name) click to toggle source

Add an counter incrementer to this element

# File lib/arboretum/doctree.rb, line 668
def count(counter_name)
  counter_name = counter_name.to_sym if !counter_name.is_a?(Symbol)
  incrementers[counter_name] = Counters::Incrementer.new(counter_name)
end
counter(counter_name) click to toggle source

Get a CounterElement associated with the incrementer of this element of the given name

# File lib/arboretum/doctree.rb, line 674
def counter(counter_name)
  counter_name = counter_name.to_sym if !counter_name.is_a?(Symbol)
  CounterElement.new(incrementers[counter_name])
end
delete()
Alias for: detach
descendants(child_list=[]) { |child| ... } click to toggle source

Get list of all this element's descendants in depth-first order

# File lib/arboretum/doctree.rb, line 879
def descendants(child_list=[])
  self.children.each do |child|
    yield child if block_given?
    child_list << child
    child.descendants(child_list)
  end
  ElementGroup.new(child_list)
end
detach() click to toggle source

Detach from current parent/siblings

# File lib/arboretum/doctree.rb, line 729
def detach
  Element.stitch!(@sibling_prev, @sibling_next)
  @parent.children.delete(self) unless @parent.nil?
  @parent, @sibling_prev, @sibling_next = nil
end
Also aliased as: prune, delete, clear, remove
find(rule_string, silent: false) { |found_element| ... } click to toggle source

Finds elements in relation to this one that fit a ScandentRule string

# File lib/arboretum/doctree.rb, line 889
def find(rule_string, silent: false)
  rule = Arboretum::Scandent::Parser.parse_rule_string(rule_string, :PATH_LOCATOR)
  selected = rule.locate(self) {|found_element| yield found_element if block_given?}
  puts "--Warning: Rule #{rule} did not match any elements!--" if selected.empty? and !silent
  ElementGroup.new(selected)
end
Also aliased as: locate
find_first(rule_string, silent: false) { |found_element| ... } click to toggle source

Find the first element in relation to this one that fits a ScandentRule string

# File lib/arboretum/doctree.rb, line 916
def find_first(rule_string, silent: false)
  self.find_first_n(rule_string, 1, :silent => silent) {|found_element| yield found_element if block_given?}.first
end
Also aliased as: locate_first
find_first_n(rule_string, limit, silent: false) { |found_element| ... } click to toggle source

Finds up to `n` elements in relation to this one that fit a ScandentRule string

# File lib/arboretum/doctree.rb, line 898
def find_first_n(rule_string, limit, silent: false)
  if limit.zero?
    puts "--Warning: Rule #{rule} was given limit '0'. Returning nil...--" if selected.empty? and !silent
    return nil
  end
  selected = []
  rule = Arboretum::Scandent::Parser.parse_rule_string(rule_string, :PATH_LOCATOR)
  rule.locate(self) do |found_element|
    return ElementGroup.new(selected) if selected.length >= limit
    yield found_element if block_given?
    selected << found_element
  end
  puts "--Warning: Rule #{rule} on #{self.to_s} did not match any elements!--" if selected.empty? and !silent
  ElementGroup.new(selected)
end
Also aliased as: locate_first_n
following_siblings() { |sibling_next| ... } click to toggle source

Get list of all of this element's following siblings in depth-first order

# File lib/arboretum/doctree.rb, line 841
def following_siblings
  sibling_list = []
  current = self
  while not current.sibling_next.nil?
    yield current.sibling_next if block_given?
    sibling_list << current.sibling_next
    current = current.sibling_next
  end
  ElementGroup.new(sibling_list)
end
graft_first_onto(graft_parent) click to toggle source

Graft onto another element of the tree as the first child

# File lib/arboretum/doctree.rb, line 771
def graft_first_onto(graft_parent)
  # Detach from current context
  self.detach

  # Update context
  @parent = graft_parent

  next_child = graft_parent.children[0]
  Element.stitch!(nil, self)
  Element.stitch!(self, next_child)

  self.update_tree_residence(graft_parent.tree_residence) if !self.tree_residence.eql?(graft_parent.tree_residence)

  # Insert graft group at the beginning of parent children
  graft_parent.children.insert(0, self)
end
graft_last_onto(graft_parent) click to toggle source

Graft onto another element of the tree as the last child

# File lib/arboretum/doctree.rb, line 789
def graft_last_onto(graft_parent)
  # Detach from current context
  self.detach

  # Update context
  @parent = graft_parent

  previous_child = graft_parent.children[-1]
  Element.stitch!(previous_child, self)
  Element.stitch!(self, nil)

  self.update_tree_residence(graft_parent.tree_residence) if !self.tree_residence.eql?(graft_parent.tree_residence)

  # Push graft group onto parent children
  graft_parent.children.push(self)
end
graft_onto(graft_parent, index=-1) click to toggle source

Graft onto another element of the tree at any index of its children By default, it will graft as the last element of the other element's children

# File lib/arboretum/doctree.rb, line 741
def graft_onto(graft_parent, index=-1)
  # If index to small or large, graft to edges of graft_parent children
  if index.abs > graft_parent.children.length
    index = graft_parent.children.length * (index > 1 ? 1 : 0)
  end
  if index == graft_parent.children.length or index == -1
    self.graft_last_onto(graft_parent)
  elsif index == 0
    self.graft_first_onto(graft_parent)
  else
    # Detach from current context
    self.detach

    # Update context
    @parent = graft_parent

    previous_child = graft_parent.children[index-1]
    Element.stitch!(previous_child, self)

    next_child = graft_parent.children[index]
    Element.stitch!(self, next_child)

    self.update_tree_residence(graft_parent.tree_residence) if !self.tree_residence.eql?(graft_parent.tree_residence)

    # Graft group at index
    graft_parent.children.insert(index, self)
  end
end
has_children?() click to toggle source
# File lib/arboretum/doctree.rb, line 926
def has_children?
  !@children.empty?
end
index_in_parent() click to toggle source
# File lib/arboretum/doctree.rb, line 631
def index_in_parent
  self.parent.children.index(self)
end
insert_child(*elements, index) { |member| ... } click to toggle source

Insert an element as a child at a specified index it this element's children

# File lib/arboretum/doctree.rb, line 564
def insert_child(*elements, index)
  placed = Array.new
  elements.each do |element|
    if !element.nil?
      element = TextElement.new(element) if element.is_a?(String)
      element = Element.create(element) if element.is_a?(Hash)
      element.graft_onto(self, index)
      element.listing.each do |member|
        yield member if block_given?
        placed << member
      end
    end
  end
  return nil if placed.empty?
  return placed.first if placed.length == 1
  return ElementGroup.new(placed)
end
inspect() click to toggle source

Temporary fix to prevent inspect from exploding due to child references Ideally will provide detailed state information rather than string output

# File lib/arboretum/doctree.rb, line 973
def inspect
  self.to_s
end
lib_add(other, category) click to toggle source

Add an element to a category in this element's library

# File lib/arboretum/doctree.rb, line 686
def lib_add(other, category)
  raise TypeError.new("Cannot create a record for a non-element") if !other.is_a?(Element) and !other.is_a?(ElementGroup)
  category = category.to_sym if !category.is_a?(Symbol)
  if @library.has_key?(category)
    if other.is_a?(Element)
      @library[category] << other
    else
      @library[category] = @library[category] + other
    end
  else
    @library[category] = ElementGroup.new([other])
  end

end
Also aliased as: allude, cite
lib_get(category) click to toggle source

Get record from the given category in this element's library

# File lib/arboretum/doctree.rb, line 704
def lib_get(category)
  category = category.to_sym if !category.is_a?(Symbol)
  @library[category]
end
Also aliased as: [], record
listing() click to toggle source

Provides a listing/array containing the element

# File lib/arboretum/doctree.rb, line 712
def listing
  [self]
end
locate(rule_string, silent: false)
Alias for: find
locate_first(rule_string, silent: false)
Alias for: find_first
locate_first_n(rule_string, limit, silent: false)
Alias for: find_first_n
matches_rule?(rule_string) click to toggle source
# File lib/arboretum/doctree.rb, line 921
def matches_rule?(rule_string)
  rule = Arboretum::Scandent::Parser.parse_rule_string(rule_string, :PATH_LISTENER)
  rule.valid_on?(self)
end
overwrite!(args) click to toggle source
# File lib/arboretum/doctree.rb, line 618
def overwrite!(args)
  replacement = Element.create(args)
  puts "Warning: Overwriting this element will delete its children" if self.has_children? and !replacement.can_have_children?
  replacement.graft_onto(self.parent, self.index_in_parent)
  self.content.graft_onto(replacement)
  replacement.break_within = self.break_within
  replacement.counters = self.counters
  replacement.resetters = self.resetters
  replacement.library = self.library
  self.detach
  replacement
end
place(*elements)
Alias for: prepend_child
preceding_siblings() { |sibling| ... } click to toggle source

Get list of all of this element's preceding siblings

# File lib/arboretum/doctree.rb, line 815
def preceding_siblings
  sibling_list = []
  current = self
  while not current.sibling_prev.nil?
    sibling_list << current.sibling_prev
    current = current.sibling_prev
  end
  sibling_list = sibling_list.reverse
  sibling_list.each {|sibling| yield sibling if block_given?}
  ElementGroup.new(sibling_list)
end
preceding_siblings_reverse() { |sibling_prev| ... } click to toggle source

Get list of all of this element's preceding siblings in reversed depth-first order Used for slightly speedier and practical searching

# File lib/arboretum/doctree.rb, line 829
def preceding_siblings_reverse
  sibling_list = []
  current = self
  while not current.sibling_prev.nil?
    yield current.sibling_prev if block_given?
    sibling_list << current.sibling_prev
    current = current.sibling_prev
  end
  ElementGroup.new(sibling_list)
end
prepend_child(*elements) { |member| ... } click to toggle source

Add an element as this element's first child

# File lib/arboretum/doctree.rb, line 543
def prepend_child(*elements)
  placed = Array.new
  elements.each do |element|
    if !element.nil?
      element = TextElement.new(element) if element.is_a?(String)
      element = Element.create(element) if element.is_a?(Hash)
      element.graft_first_onto(self)
      element.listing.each do |member|
        yield member if block_given?
        placed << member
      end
    end
  end
  return nil if placed.empty?
  return placed.first if placed.length == 1
  return ElementGroup.new(placed)
end
Also aliased as: place, unshift
prepend_sibling(*elements) { |member| ... } click to toggle source
# File lib/arboretum/doctree.rb, line 600
def prepend_sibling(*elements)
  placed = Array.new
  elements.each do |element|
    if !element.nil?
      element = TextElement.new(element) if element.is_a?(String)
      element = Element.create(element) if element.is_a?(Hash)
      element.graft_onto(self.parent, self.index_in_parent)
      element.listing.each do |member|
        yield member if block_given?
        placed << member
      end
    end
  end
  return nil if placed.empty?
  return placed.first if placed.length == 1
  return ElementGroup.new(placed)
end
prune()
Alias for: detach
push(*elements)
Alias for: append_child
record(category)
Alias for: lib_get
remove()
Alias for: detach
reset(counter_name) click to toggle source

Add a counter resetter to this element

# File lib/arboretum/doctree.rb, line 680
def reset(counter_name)
  counter_name = counter_name.to_sym if !counter_name.is_a?(Symbol)
  resetters[counter_name] = Counters::Resetter.new(counter_name)
end
set_children!(other_arr) click to toggle source

Does nothing but set children of this element If used in isolation, will cause a parent <=> child mismatch in the tree Should only be used for when set operations must be performed on element children

# File lib/arboretum/doctree.rb, line 958
def set_children!(other_arr)
  @children = other_arr
  self
end
set_parent!(other) click to toggle source

Does nothing but set parent of this element If used in isolation, will cause a parent <=> child mismatch in the tree

# File lib/arboretum/doctree.rb, line 950
def set_parent!(other)
  @parent = other
  self
end
set_sibling_next!(other) click to toggle source

Does nothing but set sibling_next of this element If used in isolation, will cause a sibling mismatch in the tree

# File lib/arboretum/doctree.rb, line 936
def set_sibling_next!(other)
  @sibling_next = other
  self
end
set_sibling_prev!(other) click to toggle source

Does nothing but set sibling_prev of this element If used in isolation, will cause a sibling mismatch in the tree

# File lib/arboretum/doctree.rb, line 943
def set_sibling_prev!(other)
  @sibling_prev = other
  self
end
text_elements(text_children=[]) click to toggle source

Returns all text elements in this element's subtree Can be very expensive on large subtrees/documents, as it performs a full transversal of the subtree

# File lib/arboretum/doctree.rb, line 637
def text_elements(text_children=[])
  if self.is_a? TextElement
    text_children << self
  elsif self.is_a? TaggedElement or self.is_a? DocRootElement
    @children.each {|child| child.text_elements(text_children)}
  end
  text_children
end
text_string(full_string='') click to toggle source

Returns a string comprised of all text in this element's subtree Can be very expensive on large subtrees/documents, as it performs a full transversal of the subtree

# File lib/arboretum/doctree.rb, line 648
def text_string(full_string='')
  if self.is_a? TextElement
    full_string << self.text
  elsif self.is_a? TaggedElement or self.is_a? DocRootElement
    full_string << ' ' if self.break_within
    @children.each {|child| child.text_string(full_string)}
    full_string << ' ' if self.break_within
  end
  full_string
end
to_s() click to toggle source
# File lib/arboretum/doctree.rb, line 967
def to_s
  "<Generic_Element>"
end
to_tree() click to toggle source
# File lib/arboretum/doctree.rb, line 963
def to_tree
  Tree.new(self)
end
unshift(*elements)
Alias for: prepend_child
unwrap_children() click to toggle source

Unwrap the children of this Element, deleting it, and it's children taking its original place in the tree

# File lib/arboretum/doctree.rb, line 807
def unwrap_children
  unwrapped_elements = self.content
  unwrapped_elements.graft_onto(self.parent, self.index_in_parent)
  self.detach
  unwrapped_elements
end
update_tree_residence(update_tree) click to toggle source
# File lib/arboretum/doctree.rb, line 723
def update_tree_residence(update_tree)
  @tree_residence = update_tree
  @children.each {|child| child.update_tree_residence(update_tree)}
end