potatosalad-mongoid_acts_as_tree 0.1.9

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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@mongoid_acts_as_tree
@@ -0,0 +1,27 @@
1
+ *0.1.4 (July 5th 2010)*
2
+ *parent_id attribute accessor acts as expected
3
+
4
+ *0.1.3 (June 15th, 2010)*
5
+ *patch from joshuabowers (http://github.com/saks/mongoid_acts_as_tree/commit/a98bb62746b71692853a91de7eb35ac55c19bf11)
6
+
7
+ *0.1.2 (April 5th, 2010)*
8
+
9
+ *Dependencies updated
10
+
11
+ *0.1.1 (April 5th, 2010)*
12
+
13
+ *Imroved #children.delete()
14
+
15
+ *Add #children.clear, #children=, #children.replace
16
+
17
+ *Ruby1.8.7p174 compatible
18
+
19
+ *Ruby1.9.1p243 compatible
20
+
21
+
22
+ *0.1.0 (April 4th, 2010)*
23
+
24
+ *Support for Mongoid instead of mongo-mapper
25
+
26
+ *Proxy object #children added with methods: <<, delete
27
+
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'mongoid'
4
+ gem 'bson'
5
+ gem 'bson_ext'
6
+ gem 'shoulda'
7
+ gem 'jeweler'
@@ -0,0 +1,39 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.7)
5
+ activesupport (= 3.0.7)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.5.0)
8
+ activesupport (3.0.7)
9
+ bson (1.3.1)
10
+ bson_ext (1.3.1)
11
+ builder (2.1.2)
12
+ gemcutter (0.7.0)
13
+ git (1.2.5)
14
+ i18n (0.5.0)
15
+ jeweler (1.4.0)
16
+ gemcutter (>= 0.1.0)
17
+ git (>= 1.2.5)
18
+ rubyforge (>= 2.0.0)
19
+ json_pure (1.5.1)
20
+ mongo (1.3.1)
21
+ bson (>= 1.3.1)
22
+ mongoid (2.0.2)
23
+ activemodel (~> 3.0)
24
+ mongo (~> 1.3)
25
+ tzinfo (~> 0.3.22)
26
+ rubyforge (2.0.4)
27
+ json_pure (>= 1.1.7)
28
+ shoulda (2.11.3)
29
+ tzinfo (0.3.27)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bson
36
+ bson_ext
37
+ jeweler
38
+ mongoid
39
+ shoulda
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Jakob Vidmar
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,48 @@
1
+ = mongoid-acts_as_tree
2
+
3
+ This is an implementation of a tree structure for Mongoid.
4
+
5
+ == Installation
6
+
7
+ Install as gem
8
+
9
+ gem install mongoid_acts_as_tree
10
+
11
+ == Usage
12
+
13
+ Enable the tree functionality by declaring acts_as_tree on your model
14
+
15
+ class Category
16
+ include Mongoid::Document
17
+ include Mongoid::Acts::Tree
18
+
19
+ field :name, :type => String
20
+
21
+ acts_as_tree
22
+ end
23
+
24
+ The method accepts :parent_id_field, :path_field, :depth_field, :order as a hash.
25
+
26
+ :parent_id_field, :path_field, :depth_field => override the default field names
27
+ :order => control the order (format ['value', 'asc'] or [['field_1', 'asc'], ['field_2', 'desc']])
28
+
29
+ Check the test_tree.rb for examples.
30
+
31
+ == About bugs
32
+
33
+ Use it. If you find any bugs, contact me (if possible with a test case) or patch it yourself (see next section).
34
+
35
+ == Note on Patches/Pull Requests
36
+
37
+ * Fork the project.
38
+ * Make your feature addition or bug fix.
39
+ * Add tests for it. This is important so I don't break it in a
40
+ future version unintentionally.
41
+ * Commit, do not mess with rakefile, version, or history.
42
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
43
+ * Send me a pull request. Bonus points for topic branches.
44
+
45
+ == Copyright
46
+
47
+ Copyright (c) 2009 Jakob Vidmar. See LICENSE for details.
48
+
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "potatosalad-mongoid_acts_as_tree"
8
+ gem.summary = %Q{ActsAsTree plugin for Mongoid}
9
+ gem.description = %Q{Port of the old, venerable ActsAsTree with a bit of a twist}
10
+ gem.email = "potatosaladx@gmail.com"
11
+ gem.homepage = "http://github.com/saks/mongoid_acts_as_tree"
12
+ gem.authors = ["Jakob Vidmar, Aliaksandr Rahalevich", "Andrew Bennett"]
13
+ gem.add_dependency("mongoid", ">= 2.0.0")
14
+ gem.add_dependency("bson", ">= 0.20.1")
15
+
16
+ gem.add_development_dependency "shoulda", ">=2.10.2"
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/test_*.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "mongoid_acts_as_tree #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
54
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.9
@@ -0,0 +1,272 @@
1
+ require "mongoid"
2
+
3
+ module Mongoid
4
+ module Acts
5
+ module Tree
6
+ def self.included(model)
7
+ model.class_eval do
8
+ extend InitializerMethods
9
+ end
10
+ end
11
+
12
+ module InitializerMethods
13
+ def acts_as_tree(options = {})
14
+ options = {
15
+ :parent_id_field => "parent_id",
16
+ :path_field => "path",
17
+ :depth_field => "depth",
18
+ :class => self
19
+ }.merge(options)
20
+
21
+ class_attribute :acts_as_tree_options
22
+ self.acts_as_tree_options = options
23
+
24
+ include InstanceMethods
25
+ include Fields
26
+ extend Fields
27
+ extend ClassMethods
28
+
29
+ field parent_id_field, :type => BSON::ObjectId
30
+ field path_field, :type => Array, :default => [], :index => true
31
+ field depth_field, :type => Integer, :default => 0
32
+
33
+ self.class_eval do
34
+ define_method "#{parent_id_field}=" do | new_parent_id |
35
+ if new_parent_id.present?
36
+ new_parent = acts_as_tree_options[:class].find new_parent_id
37
+ new_parent.children.push self, false
38
+ else
39
+ self.write_attribute parent_id_field, nil
40
+ self[path_field] = []
41
+ self[depth_field] = 0
42
+ end
43
+ end
44
+ end
45
+
46
+ after_save :move_children
47
+ validate :will_save_tree
48
+ before_destroy :destroy_descendants
49
+ end
50
+ end
51
+
52
+ module ClassMethods
53
+ def roots
54
+ self.where(parent_id_field => nil).order_by tree_order
55
+ end
56
+ end
57
+
58
+ module InstanceMethods
59
+ def [](field_name)
60
+ self.send field_name
61
+ end
62
+
63
+ def []=(field_name, value)
64
+ self.send "#{field_name}=", value
65
+ end
66
+
67
+ def ==(other)
68
+ return true if other.equal?(self)
69
+ return true if other.kind_of?(acts_as_tree_options[:class]) and other._id == self._id
70
+ false
71
+ end
72
+
73
+ def will_save_tree
74
+ if @_cyclic
75
+ errors.add(:base, "Can't be children of a descendant")
76
+ end
77
+ end
78
+
79
+ def fix_position
80
+ if parent.nil?
81
+ self.write_attribute parent_id_field, nil
82
+ self[path_field] = []
83
+ self[depth_field] = 0
84
+ else
85
+ self.write_attribute parent_id_field, parent._id
86
+ self[path_field] = parent[path_field] + [parent._id]
87
+ self[depth_field] = parent[depth_field] + 1
88
+ self.save
89
+ end
90
+ end
91
+
92
+ def parent
93
+ @_parent or (self[parent_id_field].nil? ? nil : acts_as_tree_options[:class].find(self[parent_id_field]))
94
+ end
95
+
96
+ def root?
97
+ self[parent_id_field].nil?
98
+ end
99
+
100
+ def root
101
+ self[path_field].first.nil? ? self : acts_as_tree_options[:class].find(self[path_field].first)
102
+ end
103
+
104
+ def ancestors
105
+ return [] if root?
106
+ acts_as_tree_options[:class].where(:_id.in => self[path_field]).order_by(depth_field)
107
+ end
108
+
109
+ def self_and_ancestors
110
+ ancestors << self
111
+ end
112
+
113
+ def siblings
114
+ acts_as_tree_options[:class].where(:_id.ne => self._id, parent_id_field => self[parent_id_field]).order_by tree_order
115
+ end
116
+
117
+ def self_and_siblings
118
+ acts_as_tree_options[:class].where(parent_id_field => self[parent_id_field]).order_by tree_order
119
+ end
120
+
121
+ def children
122
+ Children.new self
123
+ end
124
+
125
+ def children=(new_children_list)
126
+ self.children.clear
127
+ new_children_list.each do | child |
128
+ self.children << child
129
+ end
130
+ end
131
+
132
+ alias replace children=
133
+
134
+ def descendants
135
+ return [] if new_record?
136
+ self.class.all_in(path_field => [self._id]).order_by tree_order
137
+ end
138
+
139
+ def self_and_descendants
140
+ [self] + self.descendants
141
+ end
142
+
143
+ def is_ancestor_of?(other)
144
+ other[path_field].include?(self._id)
145
+ end
146
+
147
+ def is_or_is_ancestor_of?(other)
148
+ (other == self) or is_ancestor_of?(other)
149
+ end
150
+
151
+ def is_descendant_of?(other)
152
+ self[path_field].include?(other._id)
153
+ end
154
+
155
+ def is_or_is_descendant_of?(other)
156
+ (other == self) or is_descendant_of?(other)
157
+ end
158
+
159
+ def is_sibling_of?(other)
160
+ (other != self) and (other[parent_id_field] == self[parent_id_field])
161
+ end
162
+
163
+ def is_or_is_sibling_of?(other)
164
+ (other == self) or is_sibling_of?(other)
165
+ end
166
+
167
+ def move_children
168
+
169
+ if @_will_move
170
+ @_will_move = false
171
+ self.children.each do | child |
172
+ child.fix_position
173
+ child.save
174
+ end
175
+ @_will_move = true
176
+ end
177
+ end
178
+
179
+ def destroy_descendants
180
+ self.descendants.each &:destroy
181
+ end
182
+ end
183
+
184
+ #proxy class
185
+ class Children < Array
186
+ #TODO: improve accessors to options to eliminate object[object.parent_id_field]
187
+
188
+ def initialize(owner)
189
+ @parent = owner
190
+ self.concat find_children_for_owner.to_a
191
+ end
192
+
193
+ #Add new child to list of object children
194
+ def <<(object, will_save=true)
195
+ if object.descendants.include? @parent
196
+ object.instance_variable_set :@_cyclic, true
197
+ else
198
+ object.write_attribute object.parent_id_field, @parent._id
199
+ object[object.path_field] = @parent[@parent.path_field] + [@parent._id]
200
+ object[object.depth_field] = @parent[@parent.depth_field] + 1
201
+ object.instance_variable_set :@_will_move, true
202
+ object.save if will_save
203
+ end
204
+
205
+ super(object)
206
+ end
207
+
208
+ def build(attributes)
209
+ child = @parent.class.new(attributes)
210
+ self.push child
211
+ child
212
+ end
213
+
214
+ alias create build
215
+
216
+ alias push <<
217
+
218
+ #Deletes object only from children list.
219
+ #To delete object use <tt>object.destroy</tt>.
220
+ def delete(object_or_id)
221
+ object = case object_or_id
222
+ when String, BSON::ObjectId
223
+ @parent.class.find object_or_id
224
+ else
225
+ object_or_id
226
+ end
227
+
228
+ object.write_attribute object.parent_id_field, nil
229
+ object[object.path_field] = []
230
+ object[object.depth_field] = 0
231
+ object.save
232
+
233
+ super(object)
234
+ end
235
+
236
+ #Clear children list
237
+ def clear
238
+ self.each do | child |
239
+ @parent.children.delete child
240
+ end
241
+ end
242
+
243
+ private
244
+
245
+ def find_children_for_owner
246
+ @parent.class.where(@parent.parent_id_field => @parent.id).
247
+ order_by @parent.tree_order
248
+ end
249
+
250
+ end
251
+
252
+ module Fields
253
+ def parent_id_field
254
+ acts_as_tree_options[:parent_id_field]
255
+ end
256
+
257
+ def path_field
258
+ acts_as_tree_options[:path_field]
259
+ end
260
+
261
+ def depth_field
262
+ acts_as_tree_options[:depth_field]
263
+ end
264
+
265
+ def tree_order
266
+ acts_as_tree_options[:order] or []
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
272
+
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{potatosalad-mongoid_acts_as_tree}
8
+ s.version = "0.1.9"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Jakob Vidmar, Aliaksandr Rahalevich}, %q{Andrew Bennett}]
12
+ s.date = %q{2011-06-28}
13
+ s.description = %q{Port of the old, venerable ActsAsTree with a bit of a twist}
14
+ s.email = %q{potatosaladx@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ ".rvmrc",
23
+ "CHANGELOG",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "lib/mongoid_acts_as_tree.rb",
31
+ "potatosalad-mongoid_acts_as_tree.gemspec",
32
+ "test/helper.rb",
33
+ "test/models/category.rb",
34
+ "test/models/ordered_category.rb",
35
+ "test/models/sub_category.rb",
36
+ "test/test_order.rb",
37
+ "test/test_tree.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/saks/mongoid_acts_as_tree}
40
+ s.rdoc_options = [%q{--charset=UTF-8}]
41
+ s.require_paths = [%q{lib}]
42
+ s.rubygems_version = %q{1.8.5}
43
+ s.summary = %q{ActsAsTree plugin for Mongoid}
44
+ s.test_files = [
45
+ "test/helper.rb",
46
+ "test/models/category.rb",
47
+ "test/models/ordered_category.rb",
48
+ "test/models/sub_category.rb",
49
+ "test/test_order.rb",
50
+ "test/test_tree.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<mongoid>, [">= 2.0.0"])
58
+ s.add_runtime_dependency(%q<bson>, [">= 0.20.1"])
59
+ s.add_development_dependency(%q<shoulda>, [">= 2.10.2"])
60
+ else
61
+ s.add_dependency(%q<mongoid>, [">= 2.0.0"])
62
+ s.add_dependency(%q<bson>, [">= 0.20.1"])
63
+ s.add_dependency(%q<shoulda>, [">= 2.10.2"])
64
+ end
65
+ else
66
+ s.add_dependency(%q<mongoid>, [">= 2.0.0"])
67
+ s.add_dependency(%q<bson>, [">= 0.20.1"])
68
+ s.add_dependency(%q<shoulda>, [">= 2.10.2"])
69
+ end
70
+ end
71
+
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'mongoid'
8
+
9
+ Mongoid.configure.master = Mongo::Connection.new.db('acts_as_tree-test')
10
+
11
+ Dir["#{File.dirname(__FILE__)}/models/*.rb"].each {|file| require file}
12
+
13
+ class Test::Unit::TestCase
14
+ # Drop all columns after each test case.
15
+ def teardown
16
+ Mongoid.database.collections.each do |coll|
17
+ coll.remove
18
+ end
19
+ end
20
+
21
+ # Make sure that each test case has a teardown
22
+ # method to clear the db after each test.
23
+ def inherited(base)
24
+ base.define_method teardown do
25
+ super
26
+ end
27
+ end
28
+
29
+ def eql_arrays?(first, second)
30
+ first.map{|i| i._id}.to_set == second.map{|i| i._id}.to_set
31
+ end
32
+ end
33
+
@@ -0,0 +1,12 @@
1
+ require "mongoid"
2
+ require "mongoid_acts_as_tree"
3
+
4
+ class Category
5
+ include Mongoid::Document
6
+ include Mongoid::Acts::Tree
7
+
8
+ field :name, :type => String
9
+
10
+ acts_as_tree
11
+ end
12
+
@@ -0,0 +1,13 @@
1
+ require "mongoid"
2
+ require "mongoid_acts_as_tree"
3
+
4
+ class OrderedCategory
5
+ include Mongoid::Document
6
+ include Mongoid::Acts::Tree
7
+
8
+ field :name, :type => String
9
+ field :value, :type => Integer
10
+
11
+ acts_as_tree :order => [['value', 'asc']]
12
+ end
13
+
@@ -0,0 +1,3 @@
1
+ class SubCategory < Category
2
+
3
+ end
@@ -0,0 +1,35 @@
1
+ require 'helper'
2
+ require 'set'
3
+
4
+ class TestMongoidActsAsTree < Test::Unit::TestCase
5
+ context "Ordered tree" do
6
+ setup do
7
+ @root_1 = OrderedCategory.create(:name => "Root 1", :value => 2)
8
+ @child_1 = OrderedCategory.create(:name => "Child 1", :value => 1)
9
+ @child_2 = OrderedCategory.create(:name => "Child 2", :value => 9)
10
+ @child_2_1 = OrderedCategory.create(:name => "Child 2.1", :value => 2)
11
+
12
+ @child_3 = OrderedCategory.create(:name => "Child 3", :value => 5)
13
+ @root_2 = OrderedCategory.create(:name => "Root 2", :value => 1)
14
+
15
+ @root_1.children << @child_1
16
+ @root_1.children << @child_2
17
+ @root_1.children << @child_3
18
+
19
+ @child_2.children << @child_2_1
20
+ end
21
+
22
+ should "be in order" do
23
+ assert_equal OrderedCategory.roots.to_a, [@root_2, @root_1]
24
+ assert_equal @root_1.children, [@child_1, @child_3, @child_2]
25
+
26
+ assert_equal @root_1.descendants, [@child_1, @child_2_1, @child_3, @child_2]
27
+ assert_equal @root_1.self_and_descendants, [@root_1, @child_1, @child_2_1, @child_3, @child_2]
28
+
29
+ assert_equal @child_2.siblings, [@child_1, @child_3]
30
+ assert_equal @child_2.self_and_siblings, [@child_1, @child_3, @child_2]
31
+ assert_equal @root_1.self_and_siblings, [@root_2, @root_1]
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,237 @@
1
+ require 'helper'
2
+ require 'set'
3
+
4
+ $verbose = false
5
+
6
+ class TestMongoidActsAsTree < Test::Unit::TestCase
7
+ context "Tree" do
8
+ setup do
9
+ @root_1 = Category.create(:name => "Root 1")
10
+ @child_1 = Category.create(:name => "Child 1")
11
+ @child_2 = Category.create(:name => "Child 2")
12
+ @child_2_1 = SubCategory.create(:name => "Child 2.1")
13
+
14
+ @child_3 = SubCategory.create(:name => "Child 3")
15
+ @root_2 = Category.create(:name => "Root 2")
16
+
17
+ @root_1.children << @child_1
18
+ @root_1.children << @child_2
19
+ @root_1.children << @child_3
20
+
21
+ @child_2.children << @child_2_1
22
+ end
23
+
24
+ should "add child via create or build" do
25
+ @root_1.children.build :name => "Child 2.2"
26
+ assert Category.where(:name => "Child 2.2").first.parent == @root_1
27
+ end
28
+
29
+ should "add child via <<" do
30
+ child = Category.create(:name => "Child 2.2")
31
+ @root_1.children << child
32
+ assert child.parent == @root_1
33
+ end
34
+
35
+ should "delete child" do
36
+ @root_1.children.delete @child_1
37
+ assert_equal(2, @root_1.children.size)
38
+ @root_1.children.delete @child_2.id
39
+ assert_equal(@child_3, @root_1.children.first)
40
+ end
41
+
42
+ should "clear children list" do
43
+ @root_1.children.clear
44
+ assert_equal([], @root_1.children)
45
+ end
46
+
47
+ should "replace children list" do
48
+ new_children_list = [Category.create(:name => "test 1"), Category.create(:name => "test 2")]
49
+
50
+ @root_1.children = new_children_list
51
+ assert_equal(new_children_list, @root_1.children)
52
+
53
+ @root_1.children = []
54
+ assert_equal([], @root_1.children)
55
+ end
56
+
57
+ should "have roots" do
58
+ assert eql_arrays?(Category.roots, [@root_1, @root_2])
59
+ end
60
+
61
+ should "assign parent_id" do
62
+ child = Category.create :name => 'child'
63
+ parent = Category.create :name => 'parent'
64
+
65
+ child.parent_id = parent.id
66
+ child.save
67
+
68
+ assert_equal parent.children.first.id, child.id
69
+ assert_equal parent.id, child.parent_id
70
+ assert parent.children.include? child
71
+
72
+ assert_equal 1, child.depth
73
+ assert_equal [parent.id], child.path
74
+
75
+ more_deep_child = Category.new(
76
+ :name => 'more deep child',
77
+ :parent_id => child.id
78
+ )
79
+
80
+ assert more_deep_child.new_record?
81
+ more_deep_child.save
82
+ assert !more_deep_child.new_record?
83
+
84
+ assert_equal child.children.first.id, more_deep_child.id
85
+ assert_equal child.id, more_deep_child.parent_id
86
+ assert child.children.include? more_deep_child
87
+
88
+ assert_equal 2, more_deep_child.depth
89
+ assert_equal [parent.id, child.id], more_deep_child.path
90
+
91
+ assert parent.descendants.include? child
92
+ assert parent.descendants.include? more_deep_child
93
+
94
+ assert more_deep_child.ancestors.include? child
95
+ assert more_deep_child.ancestors.include? parent
96
+ end
97
+
98
+ should "assign blank parent_id" do
99
+ @child_1.parent_id = ''
100
+ @child_1.save
101
+
102
+ assert_nil @child_1.reload.parent_id
103
+ assert_equal 0, @child_1.depth
104
+ assert_equal [], @child_1.path
105
+
106
+ @child_1.parent_id = nil
107
+ @child_1.save
108
+
109
+ assert_nil @child_1.reload.parent_id
110
+ assert_equal 0, @child_1.depth
111
+ assert_equal [], @child_1.path
112
+ end
113
+
114
+ context "node" do
115
+ should "have a root" do
116
+ assert_equal @root_1.root, @root_1
117
+ assert_not_equal @root_1.root, @root_2.root
118
+ assert_equal @root_1, @child_2_1.root
119
+ end
120
+
121
+ should "have ancestors" do
122
+ assert_equal @root_1.ancestors, []
123
+ assert_equal @child_2.ancestors, [@root_1]
124
+ assert_equal @child_2_1.ancestors, [@root_1, @child_2]
125
+ assert_equal @root_1.self_and_ancestors, [@root_1]
126
+ assert_equal @child_2.self_and_ancestors, [@root_1, @child_2]
127
+ assert_equal @child_2_1.self_and_ancestors, [@root_1, @child_2, @child_2_1]
128
+ end
129
+
130
+ should "have siblings" do
131
+ assert eql_arrays?(@root_1.siblings, [@root_2])
132
+ assert eql_arrays?(@child_2.siblings, [@child_1, @child_3])
133
+ assert eql_arrays?(@child_2_1.siblings, [])
134
+ assert eql_arrays?(@root_1.self_and_siblings, [@root_1, @root_2])
135
+ assert eql_arrays?(@child_2.self_and_siblings, [@child_1, @child_2, @child_3])
136
+ assert eql_arrays?(@child_2_1.self_and_siblings, [@child_2_1])
137
+ end
138
+
139
+ should "set depth" do
140
+ assert_equal 0, @root_1.depth
141
+ assert_equal 1, @child_1.depth
142
+ assert_equal 2, @child_2_1.depth
143
+ end
144
+
145
+ should "have children" do
146
+ assert @child_2_1.children.empty?
147
+ assert eql_arrays?(@root_1.children, [@child_1, @child_2, @child_3])
148
+ end
149
+
150
+ should "have descendants" do
151
+ assert eql_arrays?(@root_1.descendants, [@child_1, @child_2, @child_3, @child_2_1])
152
+ assert eql_arrays?(@child_2.descendants, [@child_2_1])
153
+ assert @child_2_1.descendants.empty?
154
+ assert eql_arrays?(@root_1.self_and_descendants, [@root_1, @child_1, @child_2, @child_3, @child_2_1])
155
+ assert eql_arrays?(@child_2.self_and_descendants, [@child_2, @child_2_1])
156
+ assert eql_arrays?(@child_2_1.self_and_descendants, [@child_2_1])
157
+ end
158
+
159
+ should "be able to tell if ancestor" do
160
+ assert @root_1.is_ancestor_of?(@child_1)
161
+ assert !@root_2.is_ancestor_of?(@child_2_1)
162
+ assert !@child_2.is_ancestor_of?(@child_2)
163
+
164
+ assert @root_1.is_or_is_ancestor_of?(@child_1)
165
+ assert !@root_2.is_or_is_ancestor_of?(@child_2_1)
166
+ assert @child_2.is_or_is_ancestor_of?(@child_2)
167
+ end
168
+
169
+ should "be able to tell if descendant" do
170
+ assert !@root_1.is_descendant_of?(@child_1)
171
+ assert @child_1.is_descendant_of?(@root_1)
172
+ assert !@child_2.is_descendant_of?(@child_2)
173
+
174
+ assert !@root_1.is_or_is_descendant_of?(@child_1)
175
+ assert @child_1.is_or_is_descendant_of?(@root_1)
176
+ assert @child_2.is_or_is_descendant_of?(@child_2)
177
+ end
178
+
179
+ should "be able to tell if sibling" do
180
+ assert !@root_1.is_sibling_of?(@child_1)
181
+ assert !@child_1.is_sibling_of?(@child_1)
182
+ assert !@child_2.is_sibling_of?(@child_2)
183
+
184
+ assert !@root_1.is_or_is_sibling_of?(@child_1)
185
+ assert @child_1.is_or_is_sibling_of?(@child_2)
186
+ assert @child_2.is_or_is_sibling_of?(@child_2)
187
+ end
188
+
189
+ context "when moving" do
190
+ should "recalculate path and depth" do
191
+ @child_2.children << @child_3
192
+ @child_3.save
193
+
194
+ assert @child_2.is_or_is_ancestor_of?(@child_3)
195
+ assert @child_3.is_or_is_descendant_of?(@child_2)
196
+ assert @child_2.children.include?(@child_3)
197
+ assert @child_2.descendants.include?(@child_3)
198
+ assert @child_2_1.is_or_is_sibling_of?(@child_3)
199
+ assert_equal 2, @child_3.depth
200
+ end
201
+
202
+ should "move children on save" do
203
+ @root_2.children << @child_2
204
+
205
+ @child_2_1.reload
206
+
207
+ assert @root_2.is_or_is_ancestor_of?(@child_2_1)
208
+ assert @child_2_1.is_or_is_descendant_of?(@root_2)
209
+ assert @root_2.descendants.include?(@child_2_1)
210
+ end
211
+
212
+ should "check against cyclic graph" do
213
+ @child_2_1.children << @root_1
214
+ assert !@root_1.save
215
+ end
216
+ end
217
+
218
+ should "destroy descendants when destroyed" do
219
+ @child_2.destroy
220
+ assert_nil Category.where(:id => @child_2_1._id).first
221
+ end
222
+ end
223
+
224
+ context "root node" do
225
+ should "not have a parent" do
226
+ assert_nil @root_1.parent
227
+ end
228
+ end
229
+
230
+ context "child_node" do
231
+ should "have a parent" do
232
+ assert_equal @child_2, @child_2_1.parent
233
+ end
234
+ end
235
+ end
236
+ end
237
+
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: potatosalad-mongoid_acts_as_tree
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.9
6
+ platform: ruby
7
+ authors:
8
+ - Jakob Vidmar, Aliaksandr Rahalevich
9
+ - Andrew Bennett
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-06-28 00:00:00 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: mongoid
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 2.0.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: bson
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 0.20.1
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: shoulda
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.10.2
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: Port of the old, venerable ActsAsTree with a bit of a twist
50
+ email: potatosaladx@gmail.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - LICENSE
57
+ - README.rdoc
58
+ files:
59
+ - .document
60
+ - .gitignore
61
+ - .rvmrc
62
+ - CHANGELOG
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - LICENSE
66
+ - README.rdoc
67
+ - Rakefile
68
+ - VERSION
69
+ - lib/mongoid_acts_as_tree.rb
70
+ - potatosalad-mongoid_acts_as_tree.gemspec
71
+ - test/helper.rb
72
+ - test/models/category.rb
73
+ - test/models/ordered_category.rb
74
+ - test/models/sub_category.rb
75
+ - test/test_order.rb
76
+ - test/test_tree.rb
77
+ homepage: http://github.com/saks/mongoid_acts_as_tree
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --charset=UTF-8
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.5
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: ActsAsTree plugin for Mongoid
104
+ test_files:
105
+ - test/helper.rb
106
+ - test/models/category.rb
107
+ - test/models/ordered_category.rb
108
+ - test/models/sub_category.rb
109
+ - test/test_order.rb
110
+ - test/test_tree.rb