mongoid_acts_as_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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +48 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/lib/mongoid_acts_as_tree.rb +224 -0
- data/mongoid_acts_as_tree.gemspec +63 -0
- data/test/helper.rb +33 -0
- data/test/models/category.rb +12 -0
- data/test/models/ordered_category.rb +13 -0
- data/test/test_order.rb +35 -0
- data/test/test_tree.rb +155 -0
- metadata +106 -0
data/.document
ADDED
data/.gitignore
ADDED
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.
|
data/README.rdoc
ADDED
@@ -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, 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
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "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 = "saksmlz@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/saks/mongoid_acts_as_tree"
|
12
|
+
gem.authors = ["Jakob Vidmar, Aliaksandr Rahalevich"]
|
13
|
+
gem.add_dependency("mongoid", "<= 2.0.0")
|
14
|
+
|
15
|
+
gem.add_development_dependency "shoulda", ">=2.10.2"
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "mongoid_acts_as_tree #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
55
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,224 @@
|
|
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
|
+
}.merge(options)
|
19
|
+
|
20
|
+
write_inheritable_attribute :acts_as_tree_options, options
|
21
|
+
class_inheritable_reader :acts_as_tree_options
|
22
|
+
|
23
|
+
include InstanceMethods
|
24
|
+
include Fields
|
25
|
+
extend Fields
|
26
|
+
extend ClassMethods
|
27
|
+
|
28
|
+
field parent_id_field, :type => Mongo::ObjectID
|
29
|
+
field path_field, :type => Array, :default => [], :index => true
|
30
|
+
field depth_field, :type => Integer, :default => 0
|
31
|
+
|
32
|
+
after_save :move_children
|
33
|
+
validate :will_save_tree
|
34
|
+
before_destroy :destroy_descendants
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
def roots
|
40
|
+
self.where(parent_id_field => nil).order_by tree_order
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module InstanceMethods
|
45
|
+
def [](field_name)
|
46
|
+
self.send field_name
|
47
|
+
end
|
48
|
+
|
49
|
+
def []=(field_name, value)
|
50
|
+
self.send "#{field_name}=", value
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(other)
|
54
|
+
return true if other.equal?(self)
|
55
|
+
return true if other.instance_of?(self.class) and other._id == self._id
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def will_save_tree
|
60
|
+
if @_cyclic
|
61
|
+
errors.add(:base, "Can't be children of a descendant")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def fix_position
|
66
|
+
if parent.nil?
|
67
|
+
self[parent_id_field] = nil
|
68
|
+
self[path_field] = []
|
69
|
+
self[depth_field] = 0
|
70
|
+
else
|
71
|
+
self[parent_id_field] = parent._id
|
72
|
+
self[path_field] = parent[path_field] + [parent._id]
|
73
|
+
self[depth_field] = parent[depth_field] + 1
|
74
|
+
self.save
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def parent
|
79
|
+
@_parent or (self[parent_id_field].nil? ? nil : self.class.find(self[parent_id_field]))
|
80
|
+
end
|
81
|
+
|
82
|
+
def root?
|
83
|
+
self[parent_id_field].nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
def root
|
87
|
+
self[path_field].first.nil? ? self : self.class.find(self[path_field].first)
|
88
|
+
end
|
89
|
+
|
90
|
+
def ancestors
|
91
|
+
return [] if root?
|
92
|
+
self.class.find(self[path_field])
|
93
|
+
end
|
94
|
+
|
95
|
+
def self_and_ancestors
|
96
|
+
ancestors << self
|
97
|
+
end
|
98
|
+
|
99
|
+
def siblings
|
100
|
+
self.class.where(:_id.ne => self._id, parent_id_field => self[parent_id_field]).order_by tree_order
|
101
|
+
end
|
102
|
+
|
103
|
+
def self_and_siblings
|
104
|
+
self.class.where(parent_id_field => self[parent_id_field]).order_by tree_order
|
105
|
+
end
|
106
|
+
|
107
|
+
def children
|
108
|
+
Children.new self
|
109
|
+
end
|
110
|
+
|
111
|
+
def descendants
|
112
|
+
return [] if new_record?
|
113
|
+
self.class.where(path_field => self._id).order_by tree_order
|
114
|
+
end
|
115
|
+
|
116
|
+
def self_and_descendants
|
117
|
+
[self] + self.descendants
|
118
|
+
end
|
119
|
+
|
120
|
+
def is_ancestor_of?(other)
|
121
|
+
other[path_field].include?(self._id)
|
122
|
+
end
|
123
|
+
|
124
|
+
def is_or_is_ancestor_of?(other)
|
125
|
+
(other == self) or is_ancestor_of?(other)
|
126
|
+
end
|
127
|
+
|
128
|
+
def is_descendant_of?(other)
|
129
|
+
self[path_field].include?(other._id)
|
130
|
+
end
|
131
|
+
|
132
|
+
def is_or_is_descendant_of?(other)
|
133
|
+
(other == self) or is_descendant_of?(other)
|
134
|
+
end
|
135
|
+
|
136
|
+
def is_sibling_of?(other)
|
137
|
+
(other != self) and (other[parent_id_field] == self[parent_id_field])
|
138
|
+
end
|
139
|
+
|
140
|
+
def is_or_is_sibling_of?(other)
|
141
|
+
(other == self) or is_sibling_of?(other)
|
142
|
+
end
|
143
|
+
|
144
|
+
def move_children
|
145
|
+
|
146
|
+
if @_will_move
|
147
|
+
@_will_move = false
|
148
|
+
for child in self.children
|
149
|
+
child.fix_position
|
150
|
+
child.save
|
151
|
+
end
|
152
|
+
@_will_move = true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def destroy_descendants
|
157
|
+
self.descendants.each &:destroy
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#proxy class
|
162
|
+
class Children < Array
|
163
|
+
|
164
|
+
def initialize(owner)
|
165
|
+
@parent = owner
|
166
|
+
self.concat find_children_for_owner.to_a
|
167
|
+
end
|
168
|
+
|
169
|
+
def find_children_for_owner
|
170
|
+
@parent.class.where(@parent.parent_id_field => @parent.id).
|
171
|
+
order_by @parent.tree_order
|
172
|
+
end
|
173
|
+
|
174
|
+
def <<(object)
|
175
|
+
if object.descendants.include? @parent
|
176
|
+
object.instance_variable_set :@_cyclic, true
|
177
|
+
else
|
178
|
+
object[object.parent_id_field] = @parent._id
|
179
|
+
object[object.path_field] = @parent[@parent.path_field] + [@parent._id]
|
180
|
+
object[object.depth_field] = @parent[@parent.depth_field] + 1
|
181
|
+
object.instance_variable_set :@_will_move, true
|
182
|
+
object.save
|
183
|
+
end
|
184
|
+
|
185
|
+
super(object)
|
186
|
+
end
|
187
|
+
|
188
|
+
def delete(object_or_id)
|
189
|
+
object = case object_or_id
|
190
|
+
when String
|
191
|
+
@parent.class.where(:id => object_or_id)
|
192
|
+
else
|
193
|
+
object_or_id
|
194
|
+
end
|
195
|
+
|
196
|
+
object._parent_id = nil
|
197
|
+
object._parent_ids = (object._parent_ids || []) - [@parent.id]
|
198
|
+
object.save
|
199
|
+
super(object)
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
module Fields
|
205
|
+
def parent_id_field
|
206
|
+
acts_as_tree_options[:parent_id_field]
|
207
|
+
end
|
208
|
+
|
209
|
+
def path_field
|
210
|
+
acts_as_tree_options[:path_field]
|
211
|
+
end
|
212
|
+
|
213
|
+
def depth_field
|
214
|
+
acts_as_tree_options[:depth_field]
|
215
|
+
end
|
216
|
+
|
217
|
+
def tree_order
|
218
|
+
acts_as_tree_options[:order] or []
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
@@ -0,0 +1,63 @@
|
|
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{mongoid_acts_as_tree}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jakob Vidmar, Aliaksandr Rahalevich"]
|
12
|
+
s.date = %q{2010-04-04}
|
13
|
+
s.description = %q{Port of the old, venerable ActsAsTree with a bit of a twist}
|
14
|
+
s.email = %q{saksmlz@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/mongoid_acts_as_tree.rb",
|
27
|
+
"mongoid_acts_as_tree.gemspec",
|
28
|
+
"test/helper.rb",
|
29
|
+
"test/models/category.rb",
|
30
|
+
"test/models/ordered_category.rb",
|
31
|
+
"test/test_order.rb",
|
32
|
+
"test/test_tree.rb"
|
33
|
+
]
|
34
|
+
s.homepage = %q{http://github.com/saks/mongoid_acts_as_tree}
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = %q{1.3.6}
|
38
|
+
s.summary = %q{ActsAsTree plugin for Mongoid}
|
39
|
+
s.test_files = [
|
40
|
+
"test/test_tree.rb",
|
41
|
+
"test/test_order.rb",
|
42
|
+
"test/models/category.rb",
|
43
|
+
"test/models/ordered_category.rb",
|
44
|
+
"test/helper.rb"
|
45
|
+
]
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
49
|
+
s.specification_version = 3
|
50
|
+
|
51
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
52
|
+
s.add_runtime_dependency(%q<mongoid>, ["<= 2.0.0"])
|
53
|
+
s.add_development_dependency(%q<shoulda>, [">= 2.10.2"])
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<mongoid>, ["<= 2.0.0"])
|
56
|
+
s.add_dependency(%q<shoulda>, [">= 2.10.2"])
|
57
|
+
end
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<mongoid>, ["<= 2.0.0"])
|
60
|
+
s.add_dependency(%q<shoulda>, [">= 2.10.2"])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
data/test/helper.rb
ADDED
@@ -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,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
|
+
|
data/test/test_order.rb
ADDED
@@ -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
|
+
|
data/test/test_tree.rb
ADDED
@@ -0,0 +1,155 @@
|
|
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 = Category.create(:name => "Child 2.1")
|
13
|
+
|
14
|
+
@child_3 = Category.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 <<" do
|
25
|
+
child = Category.create(:name => "Child 2.2")
|
26
|
+
@root_1.children << child
|
27
|
+
assert child.parent == @root_1
|
28
|
+
end
|
29
|
+
|
30
|
+
should "have roots" do
|
31
|
+
assert eql_arrays?(Category.roots, [@root_1, @root_2])
|
32
|
+
end
|
33
|
+
|
34
|
+
context "node" do
|
35
|
+
should "have a root" do
|
36
|
+
assert_equal @root_1.root, @root_1
|
37
|
+
assert_not_equal @root_1.root, @root_2.root
|
38
|
+
assert_equal @root_1, @child_2_1.root
|
39
|
+
end
|
40
|
+
|
41
|
+
should "have ancestors" do
|
42
|
+
assert_equal @root_1.ancestors, []
|
43
|
+
assert_equal @child_2_1.ancestors, [@root_1, @child_2]
|
44
|
+
assert_equal @root_1.self_and_ancestors, [@root_1]
|
45
|
+
assert_equal @child_2_1.self_and_ancestors, [@root_1, @child_2, @child_2_1]
|
46
|
+
end
|
47
|
+
|
48
|
+
should "have siblings" do
|
49
|
+
assert eql_arrays?(@root_1.siblings, [@root_2])
|
50
|
+
assert eql_arrays?(@child_2.siblings, [@child_1, @child_3])
|
51
|
+
assert eql_arrays?(@child_2_1.siblings, [])
|
52
|
+
assert eql_arrays?(@root_1.self_and_siblings, [@root_1, @root_2])
|
53
|
+
assert eql_arrays?(@child_2.self_and_siblings, [@child_1, @child_2, @child_3])
|
54
|
+
assert eql_arrays?(@child_2_1.self_and_siblings, [@child_2_1])
|
55
|
+
end
|
56
|
+
|
57
|
+
should "set depth" do
|
58
|
+
assert_equal 0, @root_1.depth
|
59
|
+
assert_equal 1, @child_1.depth
|
60
|
+
assert_equal 2, @child_2_1.depth
|
61
|
+
end
|
62
|
+
|
63
|
+
should "have children" do
|
64
|
+
assert @child_2_1.children.empty?
|
65
|
+
assert eql_arrays?(@root_1.children, [@child_1, @child_2, @child_3])
|
66
|
+
end
|
67
|
+
|
68
|
+
should "have descendants" do
|
69
|
+
assert eql_arrays?(@root_1.descendants, [@child_1, @child_2, @child_3, @child_2_1])
|
70
|
+
assert eql_arrays?(@child_2.descendants, [@child_2_1])
|
71
|
+
assert @child_2_1.descendants.empty?
|
72
|
+
assert eql_arrays?(@root_1.self_and_descendants, [@root_1, @child_1, @child_2, @child_3, @child_2_1])
|
73
|
+
assert eql_arrays?(@child_2.self_and_descendants, [@child_2, @child_2_1])
|
74
|
+
assert eql_arrays?(@child_2_1.self_and_descendants, [@child_2_1])
|
75
|
+
end
|
76
|
+
|
77
|
+
should "be able to tell if ancestor" do
|
78
|
+
assert @root_1.is_ancestor_of?(@child_1)
|
79
|
+
assert !@root_2.is_ancestor_of?(@child_2_1)
|
80
|
+
assert !@child_2.is_ancestor_of?(@child_2)
|
81
|
+
|
82
|
+
assert @root_1.is_or_is_ancestor_of?(@child_1)
|
83
|
+
assert !@root_2.is_or_is_ancestor_of?(@child_2_1)
|
84
|
+
assert @child_2.is_or_is_ancestor_of?(@child_2)
|
85
|
+
end
|
86
|
+
|
87
|
+
should "be able to tell if descendant" do
|
88
|
+
assert !@root_1.is_descendant_of?(@child_1)
|
89
|
+
assert @child_1.is_descendant_of?(@root_1)
|
90
|
+
assert !@child_2.is_descendant_of?(@child_2)
|
91
|
+
|
92
|
+
assert !@root_1.is_or_is_descendant_of?(@child_1)
|
93
|
+
assert @child_1.is_or_is_descendant_of?(@root_1)
|
94
|
+
assert @child_2.is_or_is_descendant_of?(@child_2)
|
95
|
+
end
|
96
|
+
|
97
|
+
should "be able to tell if sibling" do
|
98
|
+
assert !@root_1.is_sibling_of?(@child_1)
|
99
|
+
assert !@child_1.is_sibling_of?(@child_1)
|
100
|
+
assert !@child_2.is_sibling_of?(@child_2)
|
101
|
+
|
102
|
+
assert !@root_1.is_or_is_sibling_of?(@child_1)
|
103
|
+
assert @child_1.is_or_is_sibling_of?(@child_2)
|
104
|
+
assert @child_2.is_or_is_sibling_of?(@child_2)
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when moving" do
|
108
|
+
should "recalculate path and depth" do
|
109
|
+
@child_2.children << @child_3
|
110
|
+
@child_3.save
|
111
|
+
|
112
|
+
assert @child_2.is_or_is_ancestor_of?(@child_3)
|
113
|
+
assert @child_3.is_or_is_descendant_of?(@child_2)
|
114
|
+
assert @child_2.children.include?(@child_3)
|
115
|
+
assert @child_2.descendants.include?(@child_3)
|
116
|
+
assert @child_2_1.is_or_is_sibling_of?(@child_3)
|
117
|
+
assert_equal 2, @child_3.depth
|
118
|
+
end
|
119
|
+
|
120
|
+
should "move children on save" do
|
121
|
+
@root_2.children << @child_2
|
122
|
+
|
123
|
+
@child_2_1.reload
|
124
|
+
|
125
|
+
assert @root_2.is_or_is_ancestor_of?(@child_2_1)
|
126
|
+
assert @child_2_1.is_or_is_descendant_of?(@root_2)
|
127
|
+
assert @root_2.descendants.include?(@child_2_1)
|
128
|
+
end
|
129
|
+
|
130
|
+
should "check against cyclic graph" do
|
131
|
+
@child_2_1.children << @root_1
|
132
|
+
assert !@root_1.save
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
should "destroy descendants when destroyed" do
|
137
|
+
@child_2.destroy
|
138
|
+
assert_nil Category.where(:id => @child_2_1._id).first
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "root node" do
|
143
|
+
should "not have a parent" do
|
144
|
+
assert_nil @root_1.parent
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "child_node" do
|
149
|
+
should "have a parent" do
|
150
|
+
assert_equal @child_2, @child_2_1.parent
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongoid_acts_as_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jakob Vidmar, Aliaksandr Rahalevich
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-04-04 00:00:00 +03:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: mongoid
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - <=
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 2
|
29
|
+
- 0
|
30
|
+
- 0
|
31
|
+
version: 2.0.0
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: shoulda
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 2
|
43
|
+
- 10
|
44
|
+
- 2
|
45
|
+
version: 2.10.2
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Port of the old, venerable ActsAsTree with a bit of a twist
|
49
|
+
email: saksmlz@gmail.com
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files:
|
55
|
+
- LICENSE
|
56
|
+
- README.rdoc
|
57
|
+
files:
|
58
|
+
- .document
|
59
|
+
- .gitignore
|
60
|
+
- LICENSE
|
61
|
+
- README.rdoc
|
62
|
+
- Rakefile
|
63
|
+
- VERSION
|
64
|
+
- lib/mongoid_acts_as_tree.rb
|
65
|
+
- mongoid_acts_as_tree.gemspec
|
66
|
+
- test/helper.rb
|
67
|
+
- test/models/category.rb
|
68
|
+
- test/models/ordered_category.rb
|
69
|
+
- test/test_order.rb
|
70
|
+
- test/test_tree.rb
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/saks/mongoid_acts_as_tree
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options:
|
77
|
+
- --charset=UTF-8
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
requirements: []
|
95
|
+
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.3.6
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: ActsAsTree plugin for Mongoid
|
101
|
+
test_files:
|
102
|
+
- test/test_tree.rb
|
103
|
+
- test/test_order.rb
|
104
|
+
- test/models/category.rb
|
105
|
+
- test/models/ordered_category.rb
|
106
|
+
- test/helper.rb
|