rubytree 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. data/README +6 -0
  2. data/Rakefile +54 -0
  3. data/lib/tree.rb +278 -0
  4. data/test/person.rb +13 -0
  5. data/test/testtree.rb +259 -0
  6. metadata +43 -0
data/README ADDED
@@ -0,0 +1,6 @@
1
+ = Tree Implementation
2
+
3
+
4
+ Hello
5
+
6
+ == Introduction
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+
7
+ desc "Default Task"
8
+ task :default => :gem
9
+
10
+ PKG_VERSION = '0.2.2'
11
+ PKG_FILES = FileList[
12
+ '[A-Z]*',
13
+ 'lib/**/*.rb',
14
+ 'test/**/*.rb'
15
+ ]
16
+
17
+ spec = Gem::Specification.new do |s|
18
+ s.name = "rubytree"
19
+ s.version = PKG_VERSION
20
+ s.platform = Gem::Platform::RUBY
21
+ s.author = "Anupam Sengupta"
22
+ s.email = "anupamsg@gmail.com"
23
+ s.summary = "Ruby implementation of the Tree data structure."
24
+
25
+ s.description = <<-END
26
+ Provides a generic tree data structure with ability to
27
+ store keyed node elements in the tree. The implementation
28
+ mixes in the Enumerable module.
29
+ END
30
+
31
+ s.has_rdoc = true
32
+ s.extra_rdoc_files = ['README']
33
+ s.autorequire = "tree"
34
+ s.files = PKG_FILES.to_a
35
+ s.test_files = Dir.glob('test/test*.rb')
36
+ end
37
+
38
+ Rake::GemPackageTask.new(spec) do |pkg|
39
+ pkg.need_zip = true
40
+ pkg.need_tar = true
41
+ end
42
+
43
+ Rake::TestTask.new do |t|
44
+ t.libs << "test"
45
+ t.test_files = FileList['test/test*.rb']
46
+ t.verbose = true
47
+ end
48
+
49
+ Rake::RDocTask.new do |rd|
50
+ rd.rdoc_files.include("README", "lib/**/*.rb")
51
+ end
52
+
53
+
54
+
data/lib/tree.rb ADDED
@@ -0,0 +1,278 @@
1
+ #
2
+ # = tree.rb - Generic Tree implementation
3
+ #
4
+ # Provides a generic tree data structure with ability to
5
+ # store keyed node elements in the tree. The implementation
6
+ # mixes in the Enumerable module.
7
+ #
8
+ # Author:: Anupam Sengupta (anupamsg@gmail.com)
9
+ #
10
+
11
+ module Tree
12
+
13
+ # The Tree node class implementation. Mixes in the Enumerable
14
+ # module.
15
+ # == Example
16
+ #
17
+ # require 'tree'
18
+ #
19
+ # myTreeRoot = Tree::TreeNode.new("ROOT", "Root Content")
20
+ #
21
+ # myTreeRoot << Tree::TreeNode.new("CHILD1", "Child1 Content") << Tree::TreeNode.new("GRANDCHILD1", "GrandChild1 Content")
22
+ #
23
+ # myTreeRoot << Tree::TreeNode.new("CHILD2", "Child2 Content")
24
+ #
25
+ # myTreeRoot.printTree
26
+ #
27
+ # child1 = myTreeRoot["CHILD1"]
28
+ #
29
+ # grandChild1 = myTreeRoot["CHILD1"]["GRANDCHILD1"]
30
+ #
31
+ # siblingsOfChild1Array = child1.siblings
32
+ #
33
+ # immediateChildrenArray = myTreeRoot.children
34
+ #
35
+ # # Process all nodes
36
+ #
37
+ # myTreeRoot.each { |node| node.content.reverse }
38
+ #
39
+ # myTreeRoot.remove!(child1) # Remove the child
40
+ class TreeNode
41
+ include Enumerable
42
+
43
+ attr_reader :content, :name, :parent
44
+ attr_writer :content
45
+
46
+ @@fieldSep = '|'
47
+ @@recordSep = "\n"
48
+
49
+ # Constructor which expects the name of the node
50
+ #
51
+ # name of the node is expected to be unique across the
52
+ # tree.
53
+ #
54
+ # The content can be of any type, and is defaulted to _nil_.
55
+ def initialize(name, content = nil)
56
+
57
+ raise "Node name HAS to be provided" if name == nil
58
+
59
+ @name = name
60
+ @content = content
61
+
62
+ self.setAsRoot!
63
+
64
+ @childrenHash = Hash.new
65
+ @children = []
66
+ end
67
+
68
+ # Print the string representation of this node.
69
+ def to_s
70
+ s = size()
71
+ "Node ID: #{@name} Content: #{@content} Parent: " +
72
+ (isRoot?() ? "ROOT" : "#{@parent.name}") +
73
+ " Children: #{@children.length}" +
74
+ " Total Nodes: #{s}"
75
+ end
76
+
77
+ # Protected method to set the parent node.
78
+ # This method should NOT be invoked by client code.
79
+ def parent=(parent)
80
+ @parent = parent
81
+ end
82
+
83
+ # Convenience synonym for Tree#add method.
84
+ # This method allows a convenient method to add
85
+ # children hierarchies in the tree.
86
+ # E.g. root << child << grand_child
87
+ def <<(child)
88
+ add(child)
89
+ end
90
+
91
+ # Adds the specified child node to the receiver node.
92
+ # The child node's parent is set to be the receiver.
93
+ # The child is added as the last child in the current
94
+ # list of children for the receiver node.
95
+ def add(child)
96
+ raise "Child already added" if @childrenHash.has_key?(child.name)
97
+
98
+ @childrenHash[child.name] = child
99
+ @children << child
100
+ child.parent = self
101
+ return child
102
+
103
+ end
104
+
105
+ # Removes the specified child node from the receiver node.
106
+ # The removed children nodes are orphaned but available
107
+ # if an alternate reference exists.
108
+ # Returns the child node.
109
+ def remove!(child)
110
+ @childrenHash.delete(child.name)
111
+ @children.delete(child)
112
+ child.setAsRoot! unless child == nil
113
+ return child
114
+ end
115
+
116
+ # Removes this node from its parent. If this is the root node,
117
+ # then does nothing.
118
+ def removeFromParent!
119
+ @parent.remove!(self) unless isRoot?
120
+ end
121
+
122
+ # Removes all children from the receiver node.
123
+ def removeAll!
124
+ for child in @children
125
+ child.setAsRoot!
126
+ end
127
+ @childrenHash.clear
128
+ @children.clear
129
+ self
130
+ end
131
+
132
+ # Indicates whether this node has any associated content.
133
+ def hasContent?
134
+ @content != nil
135
+ end
136
+
137
+ # Private method which sets this node as a root node.
138
+ def setAsRoot!
139
+ @parent = nil
140
+ end
141
+
142
+ # Indicates whether this node is a root node. Note that
143
+ # orphaned children will also be reported as root nodes.
144
+ def isRoot?
145
+ @parent == nil
146
+ end
147
+
148
+ # Indicates whether this node has any immediate child nodes.
149
+ def hasChildren?
150
+ @children.length != 0
151
+ end
152
+
153
+ # Returns an array of all the immediate children.
154
+ # If a block is given, yields each child node to the block.
155
+ def children
156
+ if block_given?
157
+ @children.each {|child| yield child}
158
+ else
159
+ @children
160
+ end
161
+ end
162
+
163
+ # Returns every node (including the receiver node) from the
164
+ # tree to the specified block.
165
+ def each &block
166
+ yield self
167
+ children { |child| child.each(&block) }
168
+ end
169
+
170
+ # Returns the requested node from the set of immediate
171
+ # children.
172
+ #
173
+ # If the key is _numeric_, then the in-sequence array of
174
+ # children is accessed (see Tree#children).
175
+ # If the key is not _numeric_, then it is assumed to be
176
+ # the *name* of the child node to be returned.
177
+ def [](key)
178
+ raise "Key needs to be provided" if key == nil
179
+
180
+ if key.kind_of?(Integer)
181
+ @children[key]
182
+ else
183
+ @childrenHash[key]
184
+ end
185
+ end
186
+
187
+ # Returns the total number of nodes in this tree, rooted
188
+ # at the receiver node.
189
+ def size
190
+ @children.inject(1) {|sum, node| sum + node.size}
191
+ end
192
+
193
+ # Convenience synonym for Tree#size
194
+ def length
195
+ size()
196
+ end
197
+
198
+ # Pretty prints the tree starting with the receiver node.
199
+ def printTree(tab = 0)
200
+ puts((' ' * tab) + self.to_s)
201
+ children {|child| child.printTree(tab + 4)}
202
+ end
203
+
204
+ # Returns the root for this node.
205
+ def root
206
+ root = self
207
+ root = root.parent while !root.isRoot?
208
+ root
209
+ end
210
+
211
+ # Returns an array of siblings for this node.
212
+ # If a block is provided, yeilds each of the sibling
213
+ # nodes to the block.
214
+ def siblings
215
+ if block_given?
216
+ return nil if isRoot?
217
+ for sibling in parent.children
218
+ yield sibling if sibling != self
219
+ end
220
+ else
221
+ siblings = []
222
+ parent.children {|sibling| siblings << sibling if sibling != self}
223
+ siblings
224
+ end
225
+ end
226
+
227
+ # Provides a comparision operation for the nodes. Comparision
228
+ # is based on the natural character-set ordering for the
229
+ # node names.
230
+ def <=>(other)
231
+ return +1 if other == nil
232
+ self.name <=> other.name
233
+ end
234
+
235
+ # Freezes all nodes in the tree
236
+ def freezeTree!
237
+ each {|node| node.freeze}
238
+ end
239
+
240
+ # Creates a dump representation
241
+ def createDumpRep
242
+ strRep = String.new
243
+ strRep << @name << @@fieldSep << (isRoot? ? @name : @parent.name)
244
+ strRep << @@fieldSep << Marshal.dump(@content) << @@recordSep
245
+ end
246
+
247
+ def _dump(depth)
248
+ strRep = String.new
249
+ each {|node| strRep << node.createDumpRep}
250
+ strRep
251
+ end
252
+
253
+ def TreeNode.loadDumpRep(str)
254
+ nodeHash = Hash.new
255
+ rootNode = nil
256
+ str.split(@@recordSep).each do |line|
257
+ name, parent, contentStr = line.split(@@fieldSep)
258
+ content = Marshal.load(contentStr)
259
+ currentNode = Tree::TreeNode.new(name, content)
260
+ nodeHash[name] = currentNode
261
+ if name != parent # Do for a child node
262
+ nodeHash[parent].add(currentNode)
263
+ else
264
+ rootNode = currentNode
265
+ end
266
+ end
267
+ rootNode
268
+ end
269
+
270
+ def TreeNode._load(str)
271
+ loadDumpRep(str)
272
+ end
273
+
274
+ protected :parent=, :setAsRoot!
275
+ private_class_method :loadDumpRep
276
+
277
+ end
278
+ end
data/test/person.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Person
2
+ attr_reader :first, :last
3
+ attr_writer :first, :last
4
+ def initialize(first, last)
5
+ @first = first
6
+ @last = last
7
+ end
8
+
9
+ def to_s
10
+ "#@first, #@last"
11
+ end
12
+
13
+ end
data/test/testtree.rb ADDED
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'tree'
5
+ require 'person'
6
+
7
+ # Test class for the Tree node.
8
+ class TC_TreeTest < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @root = Tree::TreeNode.new("ROOT", "Root Node")
12
+
13
+ @child1 = Tree::TreeNode.new("Child1", "Child Node 1")
14
+ @child2 = Tree::TreeNode.new("Child2", "Child Node 2")
15
+ @child3 = Tree::TreeNode.new("Child3", "Child Node 3")
16
+ @child4 = Tree::TreeNode.new("Child31", "Grand Child 1")
17
+
18
+ end
19
+
20
+ def loadChildren
21
+ @root << @child1
22
+ @root << @child2
23
+ @root << @child3 << @child4
24
+ end
25
+
26
+ def teardown
27
+ @root = nil
28
+ end
29
+
30
+ def test_root_setup
31
+ assert_not_nil(@root, "Root cannot be nil")
32
+ assert_nil(@root.parent, "Parent of root node should be nil")
33
+ assert_not_nil(@root.name, "Name should not be nil")
34
+ assert_equal("ROOT", @root.name, "Name should be 'ROOT'")
35
+ assert_equal("Root Node", @root.content, "Content should be 'Root Node'")
36
+ assert(@root.isRoot?, "Should identify as root")
37
+ assert(!@root.hasChildren?, "Cannot have any children")
38
+ assert_equal(1, @root.size, "Number of nodes should be one")
39
+
40
+ assert_raise(RuntimeError) { Tree::TreeNode.new(nil) }
41
+ end
42
+
43
+ def test_root
44
+ loadChildren
45
+
46
+ assert_same(@root, @root.root, "Root's root is self")
47
+ assert_same(@root, @child1.root, "Root should be ROOT")
48
+ assert_same(@root, @child4.root, "Root should be ROOT")
49
+ end
50
+
51
+ def test_siblings
52
+ loadChildren
53
+
54
+ siblings = []
55
+ @child1.siblings { |sibling| siblings << sibling}
56
+ assert_equal(2, siblings.length, "Should have two siblings")
57
+ assert(siblings.include?(@child2), "Should have 2nd child as sibling")
58
+ assert(siblings.include?(@child3), "Should have 3rd child as sibling")
59
+
60
+ siblings.clear
61
+ siblings = @child1.siblings
62
+ assert_equal(2, siblings.length, "Should have two siblings")
63
+
64
+ siblings.clear
65
+ @child4.siblings {|sibling| siblings << sibling}
66
+ assert(siblings.empty?, "Should not have any children")
67
+
68
+ end
69
+
70
+ def test_add
71
+ assert(!@root.hasChildren?, "Should not have any children")
72
+
73
+ @root.add(@child1)
74
+
75
+ @root << @child2
76
+
77
+ assert(@root.hasChildren?, "Should have children")
78
+ assert_equal(3, @root.size, "Should have three nodes")
79
+
80
+ @root << @child3 << @child4
81
+
82
+ assert_equal(5, @root.size, "Should have five nodes")
83
+ assert_equal(2, @child3.size, "Should have two nodes")
84
+
85
+ assert_raise(RuntimeError) { @root.add(Tree::TreeNode.new(@child1.name)) }
86
+
87
+ end
88
+
89
+ def test_remove
90
+ @root << @child1
91
+ @root << @child2
92
+
93
+ assert(@root.hasChildren?, "Should have children")
94
+ assert_equal(3, @root.size, "Should have three nodes")
95
+
96
+ @root.remove!(@child1)
97
+ assert_equal(2, @root.size, "Should have two nodes")
98
+ @root.remove!(@child2)
99
+
100
+ assert(!@root.hasChildren?, "Should have no children")
101
+ assert_equal(1, @root.size, "Should have one node")
102
+
103
+ @root << @child1
104
+ @root << @child2
105
+
106
+ assert(@root.hasChildren?, "Should have children")
107
+ assert_equal(3, @root.size, "Should have three nodes")
108
+
109
+ @root.removeAll!
110
+
111
+ assert(!@root.hasChildren?, "Should have no children")
112
+ assert_equal(1, @root.size, "Should have one node")
113
+
114
+ end
115
+
116
+ def test_removeAll
117
+ loadChildren
118
+ assert(@root.hasChildren?, "Should have children")
119
+ @root.removeAll!
120
+
121
+ assert(!@root.hasChildren?, "Should have no children")
122
+ assert_equal(1, @root.size, "Should have one node")
123
+ end
124
+
125
+ def test_removeFromParent
126
+ loadChildren
127
+ assert(@root.hasChildren?, "Should have children")
128
+
129
+ child1 = @root[0]
130
+ assert_not_nil(child1, "Child 1 should exist")
131
+ assert_same(@root, child1.root, "Child 1's root should be ROOT")
132
+ assert(@root.include?(child1), "root should have child1")
133
+ child1.removeFromParent!
134
+ assert_same(child1, child1.root, "Child 1's root should be self")
135
+ assert(!@root.include?(child1), "root should not have child1")
136
+
137
+ child1.removeFromParent!
138
+ assert_same(child1, child1.root, "Child 1's root should still be self")
139
+ end
140
+
141
+ def test_children
142
+ loadChildren
143
+
144
+ assert(@root.hasChildren?, "Should have children")
145
+ assert_equal(5, @root.size, "Should have four nodes")
146
+ assert(@child3.hasChildren?, "Should have children")
147
+
148
+ children = []
149
+ for child in @root.children
150
+ children << child
151
+ end
152
+
153
+ assert_equal(3, children.length, "Should have three direct children")
154
+ assert(!children.include?(@root), "Should not have root")
155
+ assert(children.include?(@child1), "Should have child 1")
156
+ assert(children.include?(@child2), "Should have child 2")
157
+ assert(children.include?(@child3), "Should have child 3")
158
+ assert(!children.include?(@child4), "Should not have child 4")
159
+
160
+ children.clear
161
+ children = @root.children
162
+ assert_equal(3, children.length, "Should have three children")
163
+
164
+ end
165
+
166
+ def test_find
167
+ loadChildren
168
+ foundNode = @root.find { |node| node == @child2}
169
+ assert_same(@child2, foundNode, "The node should be Child 2")
170
+
171
+ foundNode = @root.find { |node| node == @child4}
172
+ assert_same(@child4, foundNode, "The node should be Child 4")
173
+
174
+ foundNode = @root.find { |node| node.name == "Child31" }
175
+ assert_same(@child4, foundNode, "The node should be Child 4")
176
+ foundNode = @root.find { |node| node.name == "NOT PRESENT" }
177
+ assert_nil(foundNode, "The node should not be found")
178
+ end
179
+
180
+ def test_each
181
+ loadChildren
182
+ assert(@root.hasChildren?, "Should have children")
183
+ assert_equal(5, @root.size, "Should have five nodes")
184
+ assert(@child3.hasChildren?, "Should have children")
185
+
186
+ nodes = []
187
+ @root.each { |node| nodes << node }
188
+
189
+ assert_equal(5, nodes.length, "Should have FIVE NODES")
190
+ assert(nodes.include?(@root), "Should have root")
191
+ assert(nodes.include?(@child1), "Should have child 1")
192
+ assert(nodes.include?(@child2), "Should have child 2")
193
+ assert(nodes.include?(@child3), "Should have child 3")
194
+ assert(nodes.include?(@child4), "Should have child 4")
195
+ end
196
+
197
+ def test_parent
198
+ loadChildren
199
+ assert_nil(@root.parent, "Root's parent should be nil")
200
+ assert_equal(@root, @child1.parent, "Parent should be root")
201
+ assert_equal(@root, @child3.parent, "Parent should be root")
202
+ assert_equal(@child3, @child4.parent, "Parent should be child3")
203
+ assert_equal(@root, @child4.parent.parent, "Parent should be root")
204
+ end
205
+
206
+ def test_indexed_access
207
+ loadChildren
208
+ assert_equal(@child1, @root[0], "Should be the first child")
209
+ assert_equal(@child4, @root[2][0], "Should be the grandchild")
210
+ assert_nil(@root["TEST"], "Should be nil")
211
+ assert_raise(RuntimeError) { @root[nil] }
212
+ end
213
+
214
+ def test_printTree
215
+ loadChildren
216
+ #puts
217
+ #@root.printTree
218
+ end
219
+
220
+ def test_dump
221
+ loadChildren
222
+
223
+ pers = Person.new("John", "Doe")
224
+ #@root.content = pers
225
+
226
+ data = Marshal.dump(@root)
227
+
228
+ newRoot = Marshal.load(data)
229
+ assert(newRoot.isRoot?, "Must be a root node")
230
+ assert_equal("ROOT", newRoot.name, "Must identify as ROOT")
231
+ assert_equal("Root Node", newRoot.content, "Must have root's content")
232
+ #assert_equal(pers.first, newRoot.content.first, "Must be the same content")
233
+ assert_equal(@child4.name, newRoot['Child3']['Child31'].name, "Must be the grand child")
234
+ end
235
+
236
+ def test_collect
237
+ loadChildren
238
+ collectArray = @root.collect do |node|
239
+ node.content = "abc"
240
+ node
241
+ end
242
+ collectArray.each {|node| assert_equal("abc", node.content, "Should be 'abc'")}
243
+ end
244
+
245
+ def test_freezeTree
246
+ loadChildren
247
+ @root.content = "ABC"
248
+ assert_equal("ABC", @root.content, "Content should be 'ABC'")
249
+ @root.freezeTree!
250
+ assert_raise(TypeError) {@root.content = "123"}
251
+ assert_raise(TypeError) {@root[0].content = "123"}
252
+ end
253
+
254
+ def test_content
255
+ pers = Person.new("John", "Doe")
256
+ @root.content = pers
257
+ assert_same(pers, @root.content, "Content should be the same")
258
+ end
259
+ end
metadata ADDED
@@ -0,0 +1,43 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.10
3
+ specification_version: 1
4
+ name: rubytree
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.2.2
7
+ date: 2006-01-02
8
+ summary: Ruby implementation of the Tree data structure.
9
+ require_paths:
10
+ - lib
11
+ email: anupamsg@gmail.com
12
+ homepage:
13
+ rubyforge_project:
14
+ description: Provides a generic tree data structure with ability to store keyed node elements in the tree. The implementation mixes in the Enumerable module.
15
+ autorequire: tree
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
25
+ version:
26
+ platform: ruby
27
+ authors:
28
+ - Anupam Sengupta
29
+ files:
30
+ - Rakefile
31
+ - README
32
+ - lib/tree.rb
33
+ - test/person.rb
34
+ - test/testtree.rb
35
+ test_files:
36
+ - test/testtree.rb
37
+ rdoc_options: []
38
+ extra_rdoc_files:
39
+ - README
40
+ executables: []
41
+ extensions: []
42
+ requirements: []
43
+ dependencies: []