Class: RichText::Node

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/richtext/node.rb

Overview

Node

A Node can have children, which themselvs can have children. A tree like structure can thus be formed by composing multiple Nodes. An example of such a tree structure can be seen below.

The Node class implements some convenience methods for iterating, left to right, over either all

- nodes in the tree
- leafs in the tree
- direct decendant of a node

In addition to having children a Node can also have attributes, represented by simple key => value pairs.

            Example Tree
                                    +--------------------------+
                 A <- Root Node     | Left to right order: ABC |
                / \                 +--------------------------+
  Leaf Node -> B   C <- Child to A
(no children)     /|\
                  ...

Direct Known Subclasses

Document::Entry

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**attributes) ⇒ Node



31
32
33
34
# File 'lib/richtext/node.rb', line 31

def initialize(**attributes)
  @children   = []
  @attributes = attributes
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



28
29
30
# File 'lib/richtext/node.rb', line 28

def attributes
  @attributes
end

Instance Method Details

#+(other) ⇒ Object

Add (+)

Combines two nodes by creating a new root and adding the two as children.



77
78
79
# File 'lib/richtext/node.rb', line 77

def +(other)
  self.class.new.tap { |root| root << self << other }
end

#<<(child) ⇒ Object

Append

Add a child to the end of the node child list. The child must be of this class to be accepted. Note that subclasses of Node will not accept regular Nodes. The method returns self so that multiple children can be added via chaining:

root << child_a << child_b


55
56
57
58
59
60
61
62
63
# File 'lib/richtext/node.rb', line 55

def <<(child)
  unless child.is_a? self.class
    raise TypeError,
          "Only objects of class #{self.class.name} can be appended"
  end

  @children << child
  self
end

#==(other) ⇒ Object

Deep equality (include children)

Returns true if the other node has the same attributes and its children are also identical.



200
201
202
203
204
205
206
# File 'lib/richtext/node.rb', line 200

def ==(other)
  # First make sure the nodes child count matches
  return false unless equal? other

  # Lastly make sure all of the children are equal
  each_child.zip(other.each_child).all? { |c| c[0] == c[1] }
end

#[](attribute) ⇒ Object

Attribute accessor

Read and write an attribute of the node. Attributes are simply key-value pairs stored internally in a hash.



123
124
125
# File 'lib/richtext/node.rb', line 123

def [](attribute)
  @attributes[attribute]
end

#[]=(attribute, value) ⇒ Object



127
128
129
# File 'lib/richtext/node.rb', line 127

def []=(attribute, value)
  @attributes[attribute] = value
end

#countObject

Count

Returns the child count of this node.



134
135
136
# File 'lib/richtext/node.rb', line 134

def count
  @children.size
end

#create_child(**attributes) ⇒ Object

Create Child

Create and append a new child, initialized with the given attributes.



68
69
70
71
72
# File 'lib/richtext/node.rb', line 68

def create_child(**attributes)
  child = self.class.new(**attributes)
  self << child
  child
end

#each {|_self| ... } ⇒ Object

Each

Iterate over each node in the tree, including self.

Yields:

  • (_self)

Yield Parameters:



84
85
86
87
88
89
90
91
92
93
# File 'lib/richtext/node.rb', line 84

def each(&block)
  return to_enum(__callee__) unless block_given?

  yield self

  @children.each do |child|
    yield child
    child.each(&block) unless child.leaf?
  end
end

#each_child(&block) ⇒ Object

Each child

Iterate over the children of this node.



115
116
117
# File 'lib/richtext/node.rb', line 115

def each_child(&block)
  @children.each(&block)
end

#each_leaf(&block) ⇒ Object

Each Leaf

Iterate over each leaf in the tree. This method will yield the leaf nodes of the tree from left to right.



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/richtext/node.rb', line 99

def each_leaf(&block)
  return to_enum(__callee__) unless block_given?
  return yield self if leaf?

  @children.each do |child|
    if child.leaf?
      yield child
    else
      child.each_leaf(&block)
    end
  end
end

#equal?(other) ⇒ Boolean

Shallow equality (exclude children)

Returns true if the other node has the exact same attributes.



192
193
194
# File 'lib/richtext/node.rb', line 192

def equal?(other)
  count == other.count && @attributes == other.attributes
end

#initialize_copy(original) ⇒ Object



36
37
38
39
# File 'lib/richtext/node.rb', line 36

def initialize_copy(original)
  @children   = original.children.map(&:dup)
  @attributes = original.attributes.dup
end

#inspectObject



208
209
210
211
212
213
214
215
216
217
218
# File 'lib/richtext/node.rb', line 208

def inspect
  children = @children.reduce('') do |s, c|
    s + "\n" + c.inspect.gsub(/(^)/) { |m| m + '  ' }
  end

  format '#<%{name} %<attrs>p:%<id>#x>%{children}',
         name: self.class.name,
         id: object_id,
         attrs: @attributes,
         children: children
end

#leaf?Boolean

Leaf?

Returns true if this node a leaf (childless) node.



44
45
46
# File 'lib/richtext/node.rb', line 44

def leaf?
  @children.empty?
end

#minimal?Boolean

Minimal?

Test if the tree under this node is minimal or not. A non minimal tree contains children which themselvs only have one child.



149
150
151
# File 'lib/richtext/node.rb', line 149

def minimal?
  all? { |node| node.count != 1 }
end

#optimizeObject



185
186
187
# File 'lib/richtext/node.rb', line 185

def optimize
  dup.optimize!
end

#optimize!Object

Optimize!

Go through each child and merge any node that a) is not a lead node and b) only has one child, with its child. The attributes of the child will override those of the parent.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/richtext/node.rb', line 158

def optimize!
  # If the node is a leaf it cannot be optimized further
  return self if leaf?

  # First optimize each of the children. If a block was
  # given each child will be yielded to it, and children
  # for which the block returns false will be removed
  if block_given?
    @children.select! { |child| yield child.optimize! }
  else
    @children.map(&:optimize!)
  end

  # If we only have one child it is superfluous and
  # should be merged. That means this node will inherrit
  # the children of the single child as well as its
  # attributes
  if count == 1
    child = @children[0]
    # Move the children over
    @children = child.children
    @attributes.merge! child.attributes
  end

  self
end

#sizeObject

Size

Returns the size of the tree where this node is the root.



141
142
143
# File 'lib/richtext/node.rb', line 141

def size
  @children.reduce(1) { |a, e| a + e.size }
end