rubytree 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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: []