mully-mongo_tree 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/Manifest +4 -0
  2. data/README.rdoc +47 -0
  3. data/Rakefile +14 -0
  4. data/lib/mongo_tree.rb +241 -0
  5. data/mongo_tree.gemspec +31 -0
  6. metadata +63 -0
data/Manifest ADDED
@@ -0,0 +1,4 @@
1
+ lib/mongo_tree.rb
2
+ Rakefile
3
+ README.rdoc
4
+ Manifest
data/README.rdoc ADDED
@@ -0,0 +1,47 @@
1
+ = MongoTree
2
+
3
+ This is a simple way to add parent / child relationships to a Rails model that uses MongoDB and the MongoRecord plugin (not the Mongo ActiveRecord plugin.)
4
+
5
+ == Installation
6
+
7
+ Gem:
8
+
9
+ sudo gem install mongo_tree
10
+
11
+ Plugin:
12
+
13
+ script/plugin install git://github.com/mully/mongo_tree.git
14
+
15
+ == Usage
16
+
17
+ To use mongo tree, simply add the word "mongo_tree" to your model.
18
+
19
+ class Taxon < MongoRecord::Base
20
+ mongo_Tree
21
+ end
22
+
23
+ Add Child:
24
+ @root = Taxon.new({:name=>"ROOT", :content=>"ROOT content"})
25
+ @root.save
26
+ @child1 = Taxon.new({:name=>"Child", :content=>"Child content"})
27
+ @root << @child1
28
+ @grandchild = Taxon.new({:name=>"GrandChild", :content=>"Grand content"})
29
+ @child1 << @grandchild
30
+
31
+ Access Children:
32
+ @root.children #access all first level childrent
33
+ @root.descendants #access all descendants (children, grandchildren, etc)
34
+ @root.parent #parent object
35
+ @root.ancestors #all ancestors (parent, grandparents, etc)
36
+
37
+ Delete Children:
38
+ @root.remove!(@child1) #remove child from root. the child will be resaved in it's own root node.
39
+
40
+ == References
41
+
42
+ http://mongodb.org
43
+ http://github.com/mongodb/mongo-activerecord-ruby/tree/master
44
+ http://www.koders.com/ruby/fid995C0ABC8DD9B624D7C4E4D9EE319F374E4CA7FA.aspx?s=cdef%3Atree#L11
45
+ http://wiki.apache.org/couchdb/How_to_store_hierarchical_data
46
+
47
+ Copyright (c) 2009 Squeejee, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('mongo_tree', '0.1.0') do |p|
6
+ p.description = "Add hierarchy tree functionality to MongoRecord (MongoDB) models."
7
+ p.url = "http://github.com/mully/mongo_tree"
8
+ p.author = "Jim Mulhollnad"
9
+ p.email = "jim@squeejee.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.development_dependencies = []
12
+ end
13
+
14
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/lib/mongo_tree.rb ADDED
@@ -0,0 +1,241 @@
1
+ module MongoTree
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ module ClassMethods
7
+ def mongo_tree(options={})
8
+ fields :name
9
+ fields :content
10
+ fields :parents
11
+
12
+ fields :created_at
13
+ fields :updated_at
14
+
15
+ class_eval <<-EOV
16
+ include MongoTree::InstanceMethods
17
+ EOV
18
+ end
19
+
20
+ end
21
+
22
+ module InstanceMethods
23
+
24
+ @@fieldSep = '|'
25
+ @@recordSep = "\n"
26
+
27
+ def initialize(options={})
28
+ super
29
+ self.set_as_root!
30
+
31
+ if self.parents.nil?
32
+ self.parents = []
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ s = size()
38
+ "Node ID: #{@name} Content: #{@content} Parent: " +
39
+ (is_root?() ? "ROOT" : "#{@parent.name}") +
40
+ " Children: #{children.length}" +
41
+ " Total Nodes: #{s}"
42
+ end
43
+
44
+ # Convenience synonym for Tree#add method.
45
+ # This method allows a convenient method to add
46
+ # children hierarchies in the tree.
47
+ # E.g. root << child << grand_child
48
+ def <<(child)
49
+ add(child)
50
+ end
51
+
52
+ # Adds the specified child node to the receiver node.
53
+ # The child node's parent is set to be the receiver.
54
+ # The child is added as the last child in the current
55
+ # list of children for the receiver node.
56
+ def add(child)
57
+ raise "Child already added" if child.parents.include?(self.id)
58
+ child.parents = []
59
+ child.parents << self.parents
60
+ child.parents << self.id
61
+ child.parents.flatten!
62
+ child.save
63
+ return child
64
+
65
+ end
66
+
67
+ # Removes the specified child node from the receiver node.
68
+ # The removed children nodes are orphaned but available
69
+ # if an alternate reference exists.
70
+ # Returns the child node.
71
+ def remove!(child)
72
+ # Make sure child belongs to the object trying to remove it
73
+ child = self.children.select {|c| c.id == child.id}.first
74
+ unless child.nil?
75
+ old_parents = child.parents
76
+ child.parents = []
77
+ child.save
78
+ # Update the parent tree of all this object's children
79
+ child.descendants.each do |descendant, level|
80
+ descendant.parents = descendant.parents - old_parents
81
+ descendant.save
82
+ end
83
+ end
84
+
85
+ return child
86
+ end
87
+
88
+ # Removes this node from its parent. If this is the root node,
89
+ # then does nothing.
90
+ def remove_from_parent!
91
+ parent.remove!(self) unless is_root?
92
+ end
93
+
94
+ # Removes all children from the receiver node.
95
+ def remove_all!
96
+ for child in children
97
+ child.remove_from_parent!
98
+ end
99
+ self
100
+ end
101
+
102
+ # Indicates whether this node has any associated content.
103
+ def has_content?
104
+ @content != nil
105
+ end
106
+
107
+ # Indicates whether this node is a root node. Note that
108
+ # orphaned children will also be reported as root nodes.
109
+ def is_root?
110
+ parents == []
111
+ end
112
+
113
+ # Indicates whether this node has any immediate child nodes.
114
+ def has_children?
115
+ children.length != 0
116
+ end
117
+
118
+ # Returns a multi-dimensional array of children objects and the level
119
+ # where level is the depth in the hierarchy.
120
+ def descendants(max_level=nil)
121
+ children=[]
122
+ self.class.find(:all).each do |obj|
123
+ if obj.parents.include?(self.id)
124
+ level = obj.parents.reverse.index(self.id)
125
+ if max_level.nil?
126
+ children << [obj, level]
127
+ else
128
+ children << [obj, level] if level <= max_level
129
+ end
130
+ end
131
+ end
132
+ return children
133
+ end
134
+
135
+ # Returns all children of an object
136
+ def children
137
+ descendants(0).map {|c| c[0]}
138
+ end
139
+
140
+ # Returns the root for this node.
141
+ def root
142
+ self.parents.blank? ? self : self.class.find(self.parents.first)
143
+ end
144
+
145
+ def parent
146
+ self.class.find(self.parents.last) rescue nil
147
+ end
148
+
149
+ def ancestors
150
+ self.class.find(self.parents)
151
+ end
152
+
153
+ # Returns every node (including the receiver node) from the
154
+ # tree to the specified block.
155
+ def each &block
156
+ yield self
157
+ children { |child| child.each(&block) }
158
+ end
159
+
160
+ # Returns the requested node from the set of immediate
161
+ # children.
162
+ #
163
+ # If the key is _numeric_, then the in-sequence array of
164
+ # children is accessed (see Tree#children).
165
+ # If the key is not _numeric_, then it is assumed to be
166
+ # the *name* of the child node to be returned.
167
+ def [](key)
168
+ raise "Key needs to be provided" if key == nil
169
+
170
+ if key.kind_of?(Integer)
171
+ children[key]
172
+ else
173
+ descendants.select {|child, level| child.name == key}.first[0]
174
+ end
175
+ end
176
+
177
+ # Returns the total number of nodes in this tree, rooted
178
+ # at the receiver node.
179
+ def size
180
+ descendants.size
181
+ end
182
+
183
+ # Convenience synonym for Tree#size
184
+ def length
185
+ size()
186
+ end
187
+
188
+ # Pretty prints the tree starting with the receiver node.
189
+ def print_tree(tab = 0)
190
+ puts((' ' * tab) + self.to_s)
191
+ children {|child| child.print_tree(tab + 4)}
192
+ end
193
+
194
+ # Returns an array of siblings for this node.
195
+ # If a block is provided, yeilds each of the sibling
196
+ # nodes to the block.
197
+ def siblings
198
+ if block_given?
199
+ return nil if is_root?
200
+ for sibling in parent.children
201
+ yield sibling if sibling != self
202
+ end
203
+ else
204
+ siblings = []
205
+ parent.children.each {|sibling| siblings << sibling if sibling != self}
206
+ siblings
207
+ end
208
+ end
209
+
210
+ # Provides a comparision operation for the nodes. Comparision
211
+ # is based on the natural character-set ordering for the
212
+ # node names.
213
+ def <=>(other)
214
+ return +1 if other == nil
215
+ self.name <=> other.name
216
+ end
217
+
218
+ # Creates a dump representation
219
+ def create_dump_rep
220
+ strRep = String.new
221
+ strRep << @name << @@fieldSep << (is_root? ? @name : parent.name)
222
+ strRep << @@fieldSep << Marshal.dump(@content) << @@recordSep
223
+ end
224
+
225
+ def _dump(depth)
226
+ strRep = String.new
227
+ each {|node| strRep << node.createDumpRep}
228
+ strRep
229
+ end
230
+
231
+
232
+ # Private method which sets this node as a root node.
233
+ def set_as_root!
234
+ parents = []
235
+ end
236
+ end
237
+ end
238
+
239
+ class MongoRecord::Base
240
+ include MongoTree
241
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{mongo_tree}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Jim Mulhollnad"]
9
+ s.date = %q{2009-04-18}
10
+ s.description = %q{Add hierarchy tree functionality to MongoRecord (MongoDB) models.}
11
+ s.email = %q{jim@squeejee.com}
12
+ s.extra_rdoc_files = ["lib/mongo_tree.rb", "README.rdoc"]
13
+ s.files = ["lib/mongo_tree.rb", "Rakefile", "README.rdoc", "Manifest", "mongo_tree.gemspec"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{http://github.com/mully/mongo_tree}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Mongo_tree", "--main", "README.rdoc"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{mongo_tree}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{Add hierarchy tree functionality to MongoRecord (MongoDB) models.}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ else
28
+ end
29
+ else
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mully-mongo_tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jim Mulhollnad
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-18 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Add hierarchy tree functionality to MongoRecord (MongoDB) models.
17
+ email: jim@squeejee.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - lib/mongo_tree.rb
24
+ - README.rdoc
25
+ files:
26
+ - lib/mongo_tree.rb
27
+ - Rakefile
28
+ - README.rdoc
29
+ - Manifest
30
+ - mongo_tree.gemspec
31
+ has_rdoc: true
32
+ homepage: http://github.com/mully/mongo_tree
33
+ post_install_message:
34
+ rdoc_options:
35
+ - --line-numbers
36
+ - --inline-source
37
+ - --title
38
+ - Mongo_tree
39
+ - --main
40
+ - README.rdoc
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "1.2"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project: mongo_tree
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: Add hierarchy tree functionality to MongoRecord (MongoDB) models.
62
+ test_files: []
63
+