mully-mongo_tree 0.1.0

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/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
+