class StringDoc::MetaNode

Lets two or more nodes to be represented as a single node in a doc, then manipulated together.

@api private

Attributes

doc[R]

@api private

internal_nodes[R]

@api private

transforms[R]

@api private

Public Class Methods

new(nodes) click to toggle source
# File lib/string_doc/meta_node.rb, line 13
def initialize(nodes)
  # Reparent nodes that belong to the same parent.
  #
  nodes.group_by { |node| node.parent }.each_pair do |parent, children|
    # If the children already belong to a meta node doc, don't reparent them again.
    #
    unless children.first.labeled?(:__meta_node)
      parent.replace_node(children.first, self)
    end

    children[1..-1].each do |node|
      # Remove the node, but don't make it appear to have been removed for transforms.
      #
      node.remove(false, false)
    end
  end

  nodes.each do |node|
    node.set_label(:__meta_node, true)
  end

  @doc = StringDoc.from_nodes(nodes)
  @transforms = { high: [], default: [], low: [] }

  @internal_nodes = nodes.dup

  @pipeline = nil
end

Public Instance Methods

after(node) click to toggle source
# File lib/string_doc/meta_node.rb, line 198
def after(node)
  @doc.append(node)
end
append(node) click to toggle source
# File lib/string_doc/meta_node.rb, line 206
def append(node)
  internal_nodes.each do |each_node|
    each_node.append(node)
  end
end
append_html(html) click to toggle source
# File lib/string_doc/meta_node.rb, line 212
def append_html(html)
  internal_nodes.each do |each_node|
    each_node.append_html(html)
  end
end
attributes() click to toggle source
# File lib/string_doc/meta_node.rb, line 121
def attributes
  MetaAttributes.new(internal_nodes.map(&:attributes))
end
before(node) click to toggle source
# File lib/string_doc/meta_node.rb, line 202
def before(node)
  @doc.prepend(node)
end
children() click to toggle source
# File lib/string_doc/meta_node.rb, line 112
def children
  internal_nodes.each_with_object(StringDoc.empty) { |node, children|
    case node.children
    when StringDoc
      children.nodes.concat(node.children.nodes)
    end
  }
end
clear() click to toggle source
# File lib/string_doc/meta_node.rb, line 194
def clear
  internal_nodes.each(&:clear)
end
delete_label(name) click to toggle source
# File lib/string_doc/meta_node.rb, line 246
def delete_label(name)
  internal_nodes.each do |each_node|
    each_node.delete_label(name)
  end
end
each(descend: false) { |self| ... } click to toggle source
# File lib/string_doc/meta_node.rb, line 252
def each(descend: false, &block)
  return enum_for(:each, descend: descend) unless block_given?

  yield self

  nodes.each do |node|
    # Yield each node that isn't an internal node (e.g. added before/after).
    #
    unless @internal_nodes.any? { |internal_node| internal_node.equal?(node) }
      case node
      when MetaNode
        node.each do |each_meta_node|
          yield each_meta_node
        end
      else
        yield node
      end
    end
  end
end
each_significant_node(type, descend: false, &block) click to toggle source
# File lib/string_doc/meta_node.rb, line 273
def each_significant_node(type, descend: false, &block)
  return enum_for(:each_significant_node, type, descend: descend) unless block_given?

  internal_nodes.each do |node|
    node.each_significant_node(type, descend: descend, &block)
  end
end
each_significant_node_with_name(type, name, descend: false, &block) click to toggle source
# File lib/string_doc/meta_node.rb, line 289
def each_significant_node_with_name(type, name, descend: false, &block)
  return enum_for(:each_significant_node_with_name, type, name, descend: descend) unless block_given?

  internal_nodes.each do |node|
    node.each_significant_node_with_name(type, name, descend: descend, &block)
  end
end
each_significant_node_without_descending_into_type(type, descend: false, &block) click to toggle source
# File lib/string_doc/meta_node.rb, line 281
def each_significant_node_without_descending_into_type(type, descend: false, &block)
  return enum_for(:each_significant_node_without_descending_into_type, type, descend: descend) unless block_given?

  internal_nodes.each do |node|
    node.each_significant_node_without_descending_into_type(type, descend: descend, &block)
  end
end
finalize_labels(keep: []) click to toggle source
# File lib/string_doc/meta_node.rb, line 91
def finalize_labels(keep: [])
  nodes.each do |node|
    node.finalize_labels(keep: keep)
  end
end
find_first_significant_node(type, descend: false) click to toggle source
# File lib/string_doc/meta_node.rb, line 297
def find_first_significant_node(type, descend: false)
  internal_nodes.each do |node|
    if found = node.find_first_significant_node(type, descend: descend)
      return found
    end
  end

  nil
end
find_significant_nodes(type, descend: false) click to toggle source
# File lib/string_doc/meta_node.rb, line 307
def find_significant_nodes(type, descend: false)
  internal_nodes.each_with_object([]) { |node, collected|
    collected.concat(node.find_significant_nodes(type, descend: descend))
  }
end
find_significant_nodes_with_name(type, name, descend: false) click to toggle source
# File lib/string_doc/meta_node.rb, line 313
def find_significant_nodes_with_name(type, name, descend: false)
  internal_nodes.each_with_object([]) { |node, collected|
    collected.concat(node.find_significant_nodes_with_name(type, name, descend: descend))
  }
end
freeze(*) click to toggle source
Calls superclass method
# File lib/string_doc/meta_node.rb, line 97
def freeze(*)
  pipeline
  super
end
html() click to toggle source
# File lib/string_doc/meta_node.rb, line 174
def html
  internal_nodes[0].html
end
html=(html) click to toggle source
# File lib/string_doc/meta_node.rb, line 178
def html=(html)
  internal_nodes.each do |node|
    node.html = html
  end
end
initialize_copy(_) click to toggle source

@api private

Calls superclass method
# File lib/string_doc/meta_node.rb, line 43
def initialize_copy(_)
  super

  nodes, internal_nodes = [], []
  @doc.nodes.each do |current_node|
    duped_node = current_node.dup
    nodes << duped_node

    if @internal_nodes.any? { |current_internal_node| current_internal_node.equal?(current_node) }
      internal_nodes << duped_node
    end
  end

  @doc = StringDoc.from_nodes(nodes)

  @transforms = @transforms.each_with_object({}) { |(key, value), hash|
    hash[key] = value.dup
  }

  @internal_nodes = internal_nodes

  @pipeline = nil
end
label(name) click to toggle source
# File lib/string_doc/meta_node.rb, line 224
def label(name)
  if node = internal_nodes.first
    node.label(name)
  else
    nil
  end
end
labeled?(name) click to toggle source
# File lib/string_doc/meta_node.rb, line 232
def labeled?(name)
  if node = internal_nodes.first
    node.labeled?(name)
  else
    false
  end
end
next_transform() click to toggle source
# File lib/string_doc/meta_node.rb, line 125
def next_transform
  pipeline.shift
end
nodes() click to toggle source

@api private

# File lib/string_doc/meta_node.rb, line 108
def nodes
  @doc.nodes
end
parent=(parent) click to toggle source

@api private

# File lib/string_doc/meta_node.rb, line 103
def parent=(parent)
  @parent = parent
end
prepend(node) click to toggle source
# File lib/string_doc/meta_node.rb, line 218
def prepend(node)
  internal_nodes.each do |each_node|
    each_node.prepend(node)
  end
end
remove(label = true, descend = true) click to toggle source
# File lib/string_doc/meta_node.rb, line 162
def remove(label = true, descend = true)
  internal_nodes.each do |node|
    node.remove(label, descend)
  end

  @internal_nodes = []
end
removed?() click to toggle source
# File lib/string_doc/meta_node.rb, line 319
def removed?
  internal_nodes.all?(&:removed?)
end
render(output = String.new, context: nil) click to toggle source

Converts the node to an xml string.

# File lib/string_doc/meta_node.rb, line 325
def render(output = String.new, context: nil)
  if transforms_itself?
    __transform(output, context: context)
  else
    nodes.each do |each_node|
      each_node.render(output, context: context)
    end
  end

  output
end
Also aliased as: to_html, to_xml
replace(replacement) click to toggle source
# File lib/string_doc/meta_node.rb, line 154
def replace(replacement)
  internal_nodes.each do |each_node|
    each_node.replace(replacement)
  end

  @internal_nodes = StringDoc.nodes_from_doc_or_string(replacement)
end
replace_children(children) click to toggle source
# File lib/string_doc/meta_node.rb, line 184
def replace_children(children)
  internal_nodes.each do |node|
    node.replace_children(children)
  end
end
set_label(name, value) click to toggle source
# File lib/string_doc/meta_node.rb, line 240
def set_label(name, value)
  internal_nodes.each do |each_node|
    each_node.set_label(name, value)
  end
end
significance?(*types) click to toggle source
# File lib/string_doc/meta_node.rb, line 148
def significance?(*types)
  internal_nodes.any? { |node|
    node.significance?(*types)
  }
end
significant?(type = nil) click to toggle source
# File lib/string_doc/meta_node.rb, line 142
def significant?(type = nil)
  internal_nodes.any? { |node|
    node.significant?(type)
  }
end
soft_copy() click to toggle source

@api private

# File lib/string_doc/meta_node.rb, line 68
def soft_copy
  instance = self.class.allocate

  nodes, internal_nodes = [], []
  @doc.nodes.each do |current_node|
    duped_node = current_node.soft_copy
    nodes << duped_node

    if @internal_nodes.any? { |current_internal_node| current_internal_node.equal?(current_node) }
      internal_nodes << duped_node
    end
  end

  instance.instance_variable_set(:@doc, StringDoc.from_nodes(nodes))
  instance.instance_variable_set(:@transforms, @transforms)

  instance.instance_variable_set(:@internal_nodes, internal_nodes)

  instance.instance_variable_set(:@pipeline, @pipeline.dup)

  instance
end
tagname() click to toggle source
# File lib/string_doc/meta_node.rb, line 190
def tagname
  internal_nodes[0].tagname
end
text() click to toggle source
# File lib/string_doc/meta_node.rb, line 170
def text
  internal_nodes[0].text
end
to_html(output = String.new, context: nil)
Alias for: render
to_s() click to toggle source

Returns the node as an xml string, without transforming.

# File lib/string_doc/meta_node.rb, line 341
def to_s
  nodes.each_with_object(String.new) do |node, string|
    string << node.to_s
  end
end
to_xml(output = String.new, context: nil)
Alias for: render
transform(priority: :default, &block) click to toggle source
# File lib/string_doc/meta_node.rb, line 129
def transform(priority: :default, &block)
  @transforms[priority] << block
  @pipeline = nil
end
transforms?() click to toggle source
# File lib/string_doc/meta_node.rb, line 134
def transforms?
  transforms_itself? || children.transforms?
end
transforms_itself?() click to toggle source
# File lib/string_doc/meta_node.rb, line 138
def transforms_itself?
  pipeline.any?
end

Private Instance Methods

__transform(string, context:) click to toggle source
# File lib/string_doc/meta_node.rb, line 353
def __transform(string, context:)
  node = if frozen?
    soft_copy
  else
    self
  end

  current = node
  while transform = node.next_transform
    return_value = transform.call(node, context, string)

    case return_value
    when NilClass
      return
    when StringDoc
      return_value.render(string, context: context); return
    when Node, MetaNode
      if return_value.removed?
        return
      else
        current = return_value
      end
    else
      string << return_value.to_s; return
    end
  end

  current.render(string, context: context)
end
pipeline() click to toggle source
# File lib/string_doc/meta_node.rb, line 349
def pipeline
  @pipeline ||= @transforms.values.flatten
end