Class: HashTree::Node

Inherits:
Object
  • Object
show all
Defined in:
lib/hash_tree.rb

Overview

The base Node type for HashTree implementations. It is not supposed to be called from user-code

Direct Known Subclasses

Map, Set

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent, key, attach: true) ⇒ Node

Returns a new instance of Node.



45
46
47
48
49
50
51
52
# File 'lib/hash_tree.rb', line 45

def initialize(parent, key, attach: true)
  @children = {}
  if attach
    parent&.do_attach(key, self)
  else
    @parent = parent
  end
end

Instance Attribute Details

#childrenObject (readonly)

Hash from key to child node



43
44
45
# File 'lib/hash_tree.rb', line 43

def children
  @children
end

#parentObject (readonly)

Parent node. nil for the root node



40
41
42
# File 'lib/hash_tree.rb', line 40

def parent
  @parent
end

Instance Method Details

#[](key) ⇒ Object

Lookup node by key



71
# File 'lib/hash_tree.rb', line 71

def [](key) @children[key] end

#aggregate(filter = lambda { |node| true }, &block) ⇒ Object

EXPERIMENTAL

Process nodes in postorder (bottom-up). The block is called with the current node and a list of aggregated children. The optional filter argument is a lambda. The lambda is called with the current node as argument and the node if skipped if the lambda returns falsy



154
155
156
157
158
159
160
# File 'lib/hash_tree.rb', line 154

def aggregate(filter = lambda { |node| true }, &block)
  if filter.nil? || filter.call(self)
    yield(self, values.map { |child| child.aggregate(filter, &block) }.compact)
  else
    nil
  end
end

#ancestors(include_self = false) ⇒ Object

List of parents from the root element down to parent. If include_self is true, also include self as the last element



93
94
95
# File 'lib/hash_tree.rb', line 93

def ancestors(include_self = false)
  (@ancestors ||= parents(false).reverse) + (include_self ? [self] : [])
end

#attach(key, child) ⇒ Object

Attach a child to self

Implementation is in #do_attach to share code with HashTree::Set#attach that only takes one parameters



58
# File 'lib/hash_tree.rb', line 58

def attach(key, child) do_attach(key, child) end

#detach(key, ignore_not_attached: false) ⇒ Object

Detach a child from self



61
62
63
64
65
66
67
68
# File 'lib/hash_tree.rb', line 61

def detach(key, ignore_not_attached: false)
  @children.key?(key) or raise Error, "Non-existing child key: #{key.inspect}"
  child = children[key]
  ignore_not_attached || child.parent or raise Error, "Child is not attached"
  child.instance_variable_set(:@parent, nil)
  @children.delete(key)
  child.send(:clear_cached_properties)
end

#dot(path_or_keys, raise_on_not_found: true) ⇒ Object

Recursively lookup object by dot-separated list of keys. Note that for this to work, key names must not contain dot characters (‘.’)

The reverse method, #path, is only defined for HashTree::Set because a HashTree::Map node doesn’t know its own key



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/hash_tree.rb', line 102

def dot(path_or_keys, raise_on_not_found: true)
  keys = path_or_keys.is_a?(String) ? path_or_keys.split(".") : path_or_keys
  key = keys.shift or return self
  if child = dot_lookup(key)
    child.send(:dot, keys, raise_on_not_found: raise_on_not_found)
  elsif raise_on_not_found
    raise KeyNotFound, "Can't lookup '#{key}' in #{self.path.inspect}"
  else
    nil
  end
end

#dot?(path_or_keys) ⇒ Boolean

True if path_or_keys address a node

Returns:

  • (Boolean)


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

def dot?(path_or_keys)
  !dot(path_or_keys, raise_on_not_found: false).nil?
end

#each(&block) ⇒ Object

List of [key, child] tuples



120
121
122
123
124
125
126
# File 'lib/hash_tree.rb', line 120

def each(&block)
  if block_given?
    children.each { |key, child| yield(key, child) }
  else
    children.each
  end
end

#key?(key) ⇒ Boolean

Returns true iff key is included in children

Returns:

  • (Boolean)


74
# File 'lib/hash_tree.rb', line 74

def key?(key) @children.key?(key) end

#keysObject

List of keys



77
# File 'lib/hash_tree.rb', line 77

def keys() @children.keys end

#parents(include_self = false) ⇒ Object

List of parents up to the root element. If include_self is true, also include self as the first element



87
88
89
# File 'lib/hash_tree.rb', line 87

def parents(include_self = false)
  (include_self ? [self] : []) + (@parents ||= (parent&.parents(true) || []))
end

#postorderObject

Return list of nodes in postorder



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

def postorder
  values.inject([]) { |a,e| a + e.postorder } + [self]
end

#preorderObject

Return list of nodes in preorder



129
130
131
# File 'lib/hash_tree.rb', line 129

def preorder
  [self] + values.inject([]) { |a,e| a + e.preorder }
end

#rootObject

The root object or self if parent is nil



83
# File 'lib/hash_tree.rb', line 83

def root() @root ||= (parent&.root || self) end

#traverse(&block) ⇒ Object

EXPERIMENTAL

Process nodes in preorder. The block is called with the current node as argument and can return true to process its child nodes or false or nil to skip them. #traverse doesn’t accumulate a result, this is the responsibily of the block



144
145
146
# File 'lib/hash_tree.rb', line 144

def traverse(&block)
  values.each { |child| child.traverse(&block) } if yield(self)
end

#valuesObject

List of values



80
# File 'lib/hash_tree.rb', line 80

def values() @children.values end