Class: Rabal::Tree

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/rabal/tree.rb

Overview

A basic Tree structure

Direct Known Subclasses

ActionTree

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Tree

Create a new Tree with the given object.to_s as its name.



31
32
33
34
35
36
# File 'lib/rabal/tree.rb', line 31

def initialize(name)
    @name       = name.to_s
    @parent     = nil
    @children   = {}
    @parameters = nil
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_id, *params, &block) ⇒ Object

Allow for a method call to cascade up the tree looking for a Tree that responds to the call.



174
175
176
177
178
179
180
181
182
# File 'lib/rabal/tree.rb', line 174

def method_missing(method_id,*params,&block)
    if not parameters.nil? and parameters.respond_to?(method_id) then
        return parameters.send(method_id, *params, &block)
    elsif not is_root? then
        @parent.send method_id, *params, &block
    else
        raise NoMethodError, "undefined method `#{method_id}' for #{name}:Tree"
    end
end

Instance Attribute Details

#childrenObject

The children of this node. If this Hash is empty, then this Tree is a leaf.



20
21
22
# File 'lib/rabal/tree.rb', line 20

def children
  @children
end

#nameObject

The name of this Tree



12
13
14
# File 'lib/rabal/tree.rb', line 12

def name
  @name
end

#parametersObject

An abstract data point that can be utilized by child classes for whatever they like. If this is non-nil and responds to method calls it will be searched as part of the ‘method_missing’ protocol.



26
27
28
# File 'lib/rabal/tree.rb', line 26

def parameters
  @parameters
end

#parentObject

The parent of this node. If this is nil then this Tree is a root.



16
17
18
# File 'lib/rabal/tree.rb', line 16

def parent
  @parent
end

Instance Method Details

#<<(subtree) ⇒ Object Also known as: add

Add the given object to the Tree as a child of this node. If the given object is not a Tree then wrap it with a Tree.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/rabal/tree.rb', line 115

def <<(subtree)
    # this should not generally be the case, but wrap things
    # up to be nice.
    if not subtree.kind_of?(Tree) then
        subtree = Tree.new(subtree)
    end

    subtree.parent = self
   
    # FIXME: techinically this should no longer be called 'post_add' 
    # but maybe 'add_hook'  
    subtree.post_add

    # Don't overwrite any existing children with the same name,
    # just put the new subtree's children into the children of
    # the already existing subtree.

    if children.has_key?(subtree.name) then
        subtree.children.each_pair do |subtree_child_name,subtree_child_tree|
            children[subtree.name] << subtree_child_tree
        end
    else
        children[subtree.name] = subtree
    end

    return self
end

#add_at_path(path, subtree) ⇒ Object

Attach the given tree at the indicated path. The path given is always assumed to be from the root of the Tree being attached to.

The path is given as a string separated by ‘/’. Each portion of the string is matched against the name of the particular tree.

Given :

a --- b --- c
 \ 
  d - e --- f
       \
        g - h
  • the path a/b/c will match the path to Tree c

  • the path d/e/g will not match anything as the path must start at a here

  • the path a/d/e will not match anytthin as e is not a child of d

  • the path a/d/e/g will match node g

Leading and trailing ‘/’ on the path are not necessary and removed.



92
93
94
95
96
# File 'lib/rabal/tree.rb', line 92

def add_at_path(path,subtree)
    parent_tree = tree_at_path(path)
    parent_tree << subtree
    return self
end

#current_pathObject

find the current path of the tree from root to here, return it as a ‘/’ separates string.



196
197
198
199
# File 'lib/rabal/tree.rb', line 196

def current_path
    return "*#{name}*" if is_root? 
    return ([name,parent.current_path]).flatten.reverse.join("/")
end

#depthObject

Return the distance from the root



63
64
65
66
# File 'lib/rabal/tree.rb', line 63

def depth
    return 0 if is_root?
    return (1 + @parent.depth)
end

#eachObject

Allow for Enumerable to be included. This just wraps walk.



148
149
150
# File 'lib/rabal/tree.rb', line 148

def each
    self.walk(self,lambda { |tree| yield tree }) 
end

#find_subtree(path) ⇒ Object

Given the initial path to match as an Array find the Tree that matches that path.



205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/rabal/tree.rb', line 205

def find_subtree(path)
   
    if path.empty? then
        return self
    else
        possible_child = path.shift
        if children.has_key?(possible_child) then
            children[possible_child].find_subtree(path)
        else
            raise PathNotFoundError, "`#{possible_child}' does not match anything at the current path in the Tree (#{current_path})"
        end
    end
end

#has_subtree?(path) ⇒ Boolean

Return true of false if the given subtree exists or not

Returns:

  • (Boolean)


222
223
224
225
226
227
228
229
# File 'lib/rabal/tree.rb', line 222

def has_subtree?(path)
    begin
        find_subtree(path)
        return true
    rescue PathNotFoundError
        return false
    end
end

#is_leaf?Boolean

Return true if this Tree has no children.

Returns:

  • (Boolean)


41
42
43
# File 'lib/rabal/tree.rb', line 41

def is_leaf?
    @children.empty?
end

#is_root?Boolean

Return true if this Tree has no parent.

Returns:

  • (Boolean)


48
49
50
# File 'lib/rabal/tree.rb', line 48

def is_root?
    @parent.nil?
end

#post_addObject

allow for a hook so newly added Tree objects may do custom processing after being added to the tree, but before the tree is walked or processed



190
191
# File 'lib/rabal/tree.rb', line 190

def post_add
end

#rootObject

Return the root node of the tree



55
56
57
58
# File 'lib/rabal/tree.rb', line 55

def root
    return self if is_root?
    return @parent.root
end

#sizeObject

Count how many items are in the tree



155
156
157
# File 'lib/rabal/tree.rb', line 155

def size
    inject(0) { |count,n| count + 1 }
end

#tree_at_path(path_str) ⇒ Object

Return the Tree that resides at the given path



102
103
104
105
106
107
108
109
# File 'lib/rabal/tree.rb', line 102

def tree_at_path(path_str)
    path_str = path_str.chomp("/").reverse.chomp("/").reverse
    path     = path_str.split("/")

    # strip of the redundant first match if it is the same as
    # the current node
    find_subtree(path)
end

#walk(tree, method) ⇒ Object

Walk the tree in a depth first manner, visiting the Tree first, then its children



163
164
165
166
167
168
# File 'lib/rabal/tree.rb', line 163

def walk(tree,method)
    method.call(tree)
    tree.children.each_pair do |name,child|
        walk(child,method) 
    end
end