closure_tree 3.0.4 → 3.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/README.md +10 -0
- data/lib/closure_tree/acts_as_tree.rb +25 -12
- data/lib/closure_tree/version.rb +1 -1
- data/spec/tag_spec.rb +23 -5
- data/spec/user_spec.rb +11 -8
- metadata +4 -4
data/README.md
CHANGED
@@ -9,6 +9,12 @@ See [Bill Karwin](http://karwin.blogspot.com/)'s excellent
|
|
9
9
|
[Models for hierarchical data presentation](http://www.slideshare.net/billkarwin/models-for-hierarchical-data)
|
10
10
|
for a description of different tree storage algorithms.
|
11
11
|
|
12
|
+
Closure tree is [tested under every combination](https://secure.travis-ci.org/mceachen/closure_tree.png?branch=master) of
|
13
|
+
|
14
|
+
* Ruby 1.8.7 and Ruby 1.9.3
|
15
|
+
* The latest Rails 3.0, 3.1, and 3.2 branches, and
|
16
|
+
* Using MySQL, Postgresql, and SQLite.
|
17
|
+
|
12
18
|
## Installation
|
13
19
|
|
14
20
|
Note that closure_tree only supports Rails 3.0 and later, and has test coverage for MySQL, PostgreSQL, and SQLite.
|
@@ -181,6 +187,10 @@ class WhatTag < Tag ; end
|
|
181
187
|
|
182
188
|
## Change log
|
183
189
|
|
190
|
+
### 3.1.0
|
191
|
+
|
192
|
+
* Switched to using ```has_many :though``` rather than ```has_and_belongs_to_many```
|
193
|
+
|
184
194
|
### 3.0.4
|
185
195
|
|
186
196
|
* Merged [pull request](https://github.com/mceachen/closure_tree/pull/8) to fix ```.siblings``` and ```.self_and_siblings```
|
@@ -24,6 +24,11 @@ module ClosureTree
|
|
24
24
|
belongs_to :ancestor, :class_name => "#{ct_class.to_s}"
|
25
25
|
belongs_to :descendant, :class_name => "#{ct_class.to_s}"
|
26
26
|
attr_accessible :ancestor, :descendant, :generations
|
27
|
+
def ==(comparison_object)
|
28
|
+
comparison_object.instance_of?(self.class) &&
|
29
|
+
self.attributes == comparison_object.attributes
|
30
|
+
end
|
31
|
+
alias :eql? :==
|
27
32
|
RUBY
|
28
33
|
|
29
34
|
include ClosureTree::Model
|
@@ -42,18 +47,26 @@ module ClosureTree
|
|
42
47
|
:foreign_key => parent_column_name,
|
43
48
|
:dependent => closure_tree_options[:dependent]
|
44
49
|
|
45
|
-
|
46
|
-
:class_name =>
|
47
|
-
:join_table => hierarchy_table_name,
|
50
|
+
has_many :ancestor_hierarchies,
|
51
|
+
:class_name => hierarchy_class_name,
|
48
52
|
:foreign_key => "descendant_id",
|
49
|
-
:
|
53
|
+
:order => "generations asc",
|
54
|
+
:dependent => :destroy
|
55
|
+
|
56
|
+
has_many :self_and_ancestors,
|
57
|
+
:through => :ancestor_hierarchies,
|
58
|
+
:source => :ancestor,
|
50
59
|
:order => "generations asc"
|
51
60
|
|
52
|
-
|
53
|
-
:class_name =>
|
54
|
-
:join_table => hierarchy_table_name,
|
61
|
+
has_many :descendant_hierarchies,
|
62
|
+
:class_name => hierarchy_class_name,
|
55
63
|
:foreign_key => "ancestor_id",
|
56
|
-
:
|
64
|
+
:order => "generations asc",
|
65
|
+
:dependent => :destroy
|
66
|
+
|
67
|
+
has_many :self_and_descendants,
|
68
|
+
:through => :descendant_hierarchies,
|
69
|
+
:source => :descendant,
|
57
70
|
:order => "generations asc"
|
58
71
|
|
59
72
|
scope :roots, where(parent_column_name => nil)
|
@@ -71,7 +84,7 @@ module ClosureTree
|
|
71
84
|
|
72
85
|
# Returns true if this node has no parents.
|
73
86
|
def root?
|
74
|
-
|
87
|
+
_parent_id.nil?
|
75
88
|
end
|
76
89
|
|
77
90
|
# Returns true if this node has a parent, and is not a root.
|
@@ -86,7 +99,7 @@ module ClosureTree
|
|
86
99
|
|
87
100
|
# Returns the farthest ancestor, or self if +root?+
|
88
101
|
def root
|
89
|
-
|
102
|
+
self_and_ancestors.where(parent_column_name.to_sym => nil).first
|
90
103
|
end
|
91
104
|
|
92
105
|
def leaves
|
@@ -136,7 +149,7 @@ module ClosureTree
|
|
136
149
|
|
137
150
|
# Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+.
|
138
151
|
def find_by_path(path)
|
139
|
-
path =
|
152
|
+
path = path.is_a?(Enumerable) ? path.dup : [path]
|
140
153
|
node = self
|
141
154
|
while !path.empty? && node
|
142
155
|
node = node.children.send("find_by_#{name_column}", path.shift)
|
@@ -146,7 +159,7 @@ module ClosureTree
|
|
146
159
|
|
147
160
|
# Find a child node whose +ancestry_path+ minus self.ancestry_path is +path+
|
148
161
|
def find_or_create_by_path(path, attributes = {})
|
149
|
-
path =
|
162
|
+
path = path.is_a?(Enumerable) ? path.dup : [path]
|
150
163
|
node = self
|
151
164
|
attrs = {}
|
152
165
|
attrs[:type] = self.type if ct_subclass? && ct_has_type?
|
data/lib/closure_tree/version.rb
CHANGED
data/spec/tag_spec.rb
CHANGED
@@ -6,7 +6,6 @@ shared_examples_for Tag do
|
|
6
6
|
def nuke_db
|
7
7
|
Tag.delete_all
|
8
8
|
TagHierarchy.delete_all
|
9
|
-
DestroyedTag.delete_all
|
10
9
|
end
|
11
10
|
|
12
11
|
before :each do
|
@@ -42,6 +41,7 @@ shared_examples_for Tag do
|
|
42
41
|
@root = Tag.create! :name => "root"
|
43
42
|
@mid = @root.children.create! :name => "mid"
|
44
43
|
@leaf = @mid.children.create! :name => "leaf"
|
44
|
+
DestroyedTag.delete_all
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should create all tags" do
|
@@ -100,6 +100,23 @@ shared_examples_for Tag do
|
|
100
100
|
@root.children << @leaf
|
101
101
|
Tag.leaves.should =~ [@leaf, @mid]
|
102
102
|
end
|
103
|
+
|
104
|
+
it "cleans up hierarchy references for leaves" do
|
105
|
+
@leaf.destroy
|
106
|
+
TagHierarchy.find_all_by_ancestor_id(@leaf.id).should be_empty
|
107
|
+
TagHierarchy.find_all_by_descendant_id(@leaf.id).should be_empty
|
108
|
+
end
|
109
|
+
|
110
|
+
it "cleans up hierarchy references" do
|
111
|
+
@mid.destroy
|
112
|
+
TagHierarchy.find_all_by_ancestor_id(@mid.id).should be_empty
|
113
|
+
TagHierarchy.find_all_by_descendant_id(@mid.id).should be_empty
|
114
|
+
@root.reload.should be_root
|
115
|
+
root_hiers = @root.ancestor_hierarchies.to_a
|
116
|
+
root_hiers.size.should == 1
|
117
|
+
TagHierarchy.find_all_by_ancestor_id(@root.id).should == root_hiers
|
118
|
+
TagHierarchy.find_all_by_descendant_id(@root.id).should == root_hiers
|
119
|
+
end
|
103
120
|
end
|
104
121
|
end
|
105
122
|
|
@@ -109,6 +126,7 @@ shared_examples_for Tag do
|
|
109
126
|
|
110
127
|
before :each do
|
111
128
|
Tag.rebuild!
|
129
|
+
DestroyedTag.delete_all
|
112
130
|
end
|
113
131
|
|
114
132
|
context "class injection" do
|
@@ -199,8 +217,8 @@ shared_examples_for Tag do
|
|
199
217
|
l1 = Tag.find_or_create_by_path(%w{roottest1 branch1 leaf1})
|
200
218
|
l2 = Tag.find_or_create_by_path(%w{roottest2 branch2 leaf2})
|
201
219
|
l1.children << l2.root
|
202
|
-
l1.ancestry_path.should == %w{roottest1 branch1 leaf1}
|
203
|
-
l2.ancestry_path.should == %w{roottest1 branch1 leaf1 roottest2 branch2 leaf2}
|
220
|
+
l1.reload.ancestry_path.should == %w{roottest1 branch1 leaf1}
|
221
|
+
l2.reload.ancestry_path.should == %w{roottest1 branch1 leaf1 roottest2 branch2 leaf2}
|
204
222
|
end
|
205
223
|
|
206
224
|
it "should cascade delete all children" do
|
@@ -238,12 +256,12 @@ shared_examples_for Tag do
|
|
238
256
|
tags(:a1).self_and_siblings.to_a.should =~ Tag.roots.to_a
|
239
257
|
end
|
240
258
|
|
241
|
-
it "
|
259
|
+
it "assembles ancestors" do
|
242
260
|
tags(:child).ancestors.should == [tags(:parent), tags(:grandparent)]
|
243
261
|
tags(:child).self_and_ancestors.should == [tags(:child), tags(:parent), tags(:grandparent)]
|
244
262
|
end
|
245
263
|
|
246
|
-
it "
|
264
|
+
it "assembles descendants" do
|
247
265
|
tags(:parent).descendants.should == [tags(:child)]
|
248
266
|
tags(:parent).self_and_descendants.should == [tags(:parent), tags(:child)]
|
249
267
|
tags(:grandparent).descendants.should == [tags(:parent), tags(:child)]
|
data/spec/user_spec.rb
CHANGED
@@ -37,6 +37,7 @@ describe "empty db" do
|
|
37
37
|
@root = User.create! :email => "poppy@t.co"
|
38
38
|
@mid = @root.children.create! :email => "matt@t.co"
|
39
39
|
@leaf = @mid.children.create! :email => "james@t.co"
|
40
|
+
@root_id = @root.id
|
40
41
|
end
|
41
42
|
|
42
43
|
it "should create all Users" do
|
@@ -63,13 +64,15 @@ describe "empty db" do
|
|
63
64
|
@root.destroy
|
64
65
|
assert_mid_and_leaf_remain
|
65
66
|
end
|
66
|
-
end
|
67
|
-
end
|
68
67
|
|
69
|
-
def assert_mid_and_leaf_remain
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
68
|
+
def assert_mid_and_leaf_remain
|
69
|
+
ReferralHierarchy.find_all_by_ancestor_id(@root_id).should be_empty
|
70
|
+
ReferralHierarchy.find_all_by_descendant_id(@root_id).should be_empty
|
71
|
+
@mid.ancestry_path.should == %w{matt@t.co}
|
72
|
+
@leaf.ancestry_path.should == %w{matt@t.co james@t.co}
|
73
|
+
@mid.self_and_descendants.should =~ [@mid, @leaf]
|
74
|
+
User.roots.should == [@mid]
|
75
|
+
User.leaves.should == [@leaf]
|
76
|
+
end
|
77
|
+
end
|
75
78
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: closure_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -192,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
192
|
version: '0'
|
193
193
|
segments:
|
194
194
|
- 0
|
195
|
-
hash:
|
195
|
+
hash: 1583374080415694998
|
196
196
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
197
|
none: false
|
198
198
|
requirements:
|
@@ -201,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
201
|
version: '0'
|
202
202
|
segments:
|
203
203
|
- 0
|
204
|
-
hash:
|
204
|
+
hash: 1583374080415694998
|
205
205
|
requirements: []
|
206
206
|
rubyforge_project:
|
207
207
|
rubygems_version: 1.8.21
|