closure_tree 3.7.1 → 3.7.2
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 +6 -1
- data/lib/closure_tree/acts_as_tree.rb +9 -5
- data/lib/closure_tree/version.rb +1 -1
- data/spec/db/schema.rb +16 -0
- data/spec/node_spec.rb +148 -0
- data/spec/support/models.rb +13 -1
- metadata +22 -4
data/README.md
CHANGED
@@ -396,6 +396,12 @@ Parallelism is not tested with Rails 3.0.x nor 3.1.x due to this
|
|
396
396
|
|
397
397
|
## Change log
|
398
398
|
|
399
|
+
### 3.7.2
|
400
|
+
|
401
|
+
* Support for UUID primary keys. Addresses
|
402
|
+
[issue 40](https://github.com/mceachen/closure_tree/issues/40). Thanks for the pull request,
|
403
|
+
[Julien](https://github.com/calexicoz)!
|
404
|
+
|
399
405
|
### 3.7.1
|
400
406
|
|
401
407
|
* Moved requires into ActiveSupport.on_load
|
@@ -550,4 +556,3 @@ a.save
|
|
550
556
|
* https://github.com/patshaughnessy/class_factory
|
551
557
|
* JetBrains, which provides an [open-source license](http://www.jetbrains.com/ruby/buy/buy.jsp#openSource) to
|
552
558
|
[RubyMine](http://www.jetbrains.com/ruby/features/) for the development of this project.
|
553
|
-
|
@@ -237,7 +237,7 @@ module ClosureTree
|
|
237
237
|
INNER JOIN (
|
238
238
|
SELECT descendant_id
|
239
239
|
FROM #{quoted_hierarchy_table_name}
|
240
|
-
WHERE ancestor_id = #{self.id}
|
240
|
+
WHERE ancestor_id = #{ct_quote(self.id)}
|
241
241
|
GROUP BY 1
|
242
242
|
HAVING MAX(#{quoted_hierarchy_table_name}.generations) = #{generation_level.to_i}
|
243
243
|
) AS descendants ON (#{quoted_table_name}.#{ct_base_class.primary_key} = descendants.descendant_id)
|
@@ -290,9 +290,9 @@ module ClosureTree
|
|
290
290
|
connection.execute <<-SQL
|
291
291
|
INSERT INTO #{quoted_hierarchy_table_name}
|
292
292
|
(ancestor_id, descendant_id, generations)
|
293
|
-
SELECT x.ancestor_id, #{id}, x.generations + 1
|
293
|
+
SELECT x.ancestor_id, #{ct_quote(id)}, x.generations + 1
|
294
294
|
FROM #{quoted_hierarchy_table_name} x
|
295
|
-
WHERE x.descendant_id = #{self.ct_parent_id}
|
295
|
+
WHERE x.descendant_id = #{ct_quote(self.ct_parent_id)}
|
296
296
|
SQL
|
297
297
|
end
|
298
298
|
children.each { |c| c.rebuild! }
|
@@ -316,9 +316,9 @@ module ClosureTree
|
|
316
316
|
SELECT DISTINCT descendant_id
|
317
317
|
FROM ( SELECT descendant_id
|
318
318
|
FROM #{quoted_hierarchy_table_name}
|
319
|
-
WHERE ancestor_id = #{id}
|
319
|
+
WHERE ancestor_id = #{ct_quote(id)}
|
320
320
|
) AS x )
|
321
|
-
OR descendant_id = #{id}
|
321
|
+
OR descendant_id = #{ct_quote(id)}
|
322
322
|
SQL
|
323
323
|
end
|
324
324
|
|
@@ -334,6 +334,10 @@ module ClosureTree
|
|
334
334
|
end
|
335
335
|
end
|
336
336
|
|
337
|
+
def ct_quote(field)
|
338
|
+
self.class.connection.quote(field)
|
339
|
+
end
|
340
|
+
|
337
341
|
# TODO: _parent_id will be removed in the next major version
|
338
342
|
alias :_parent_id :ct_parent_id
|
339
343
|
|
data/lib/closure_tree/version.rb
CHANGED
data/spec/db/schema.rb
CHANGED
@@ -11,6 +11,22 @@ end
|
|
11
11
|
|
12
12
|
ActiveRecord::Schema.define(:version => 0) do
|
13
13
|
|
14
|
+
create_table "nodes", :id => false do |t|
|
15
|
+
t.string "id"
|
16
|
+
t.string "name"
|
17
|
+
t.string "parent_id"
|
18
|
+
t.datetime "created_at"
|
19
|
+
t.datetime "updated_at"
|
20
|
+
end
|
21
|
+
|
22
|
+
force_add_index "nodes", [:id], :name => "node_id", :unique => true
|
23
|
+
|
24
|
+
create_table "node_hierarchies", :id => false, :force => true do |t|
|
25
|
+
t.string "ancestor_id", :null => false
|
26
|
+
t.string "descendant_id", :null => false
|
27
|
+
t.integer "generations", :null => false
|
28
|
+
end
|
29
|
+
|
14
30
|
create_table "tags", :force => true do |t|
|
15
31
|
t.string "name"
|
16
32
|
t.string "title"
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for Node do
|
4
|
+
|
5
|
+
it "has correct accessible_attributes" do
|
6
|
+
Node.accessible_attributes.to_a.should =~ %w(parent name)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "empty db" do
|
10
|
+
|
11
|
+
def nuke_db
|
12
|
+
NodeHierarchy.delete_all
|
13
|
+
Node.delete_all
|
14
|
+
end
|
15
|
+
|
16
|
+
before :each do
|
17
|
+
nuke_db
|
18
|
+
end
|
19
|
+
|
20
|
+
context "empty db" do
|
21
|
+
it "should return no entities" do
|
22
|
+
Node.roots.should be_empty
|
23
|
+
Node.leaves.should be_empty
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "1 node db" do
|
28
|
+
it "should return the only entity as a root and leaf" do
|
29
|
+
a = Node.create!(:name => "a")
|
30
|
+
Node.roots.should == [a]
|
31
|
+
Node.leaves.should == [a]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "2 node db" do
|
36
|
+
it "should return a simple root and leaf" do
|
37
|
+
root = Node.create!(:name => "root")
|
38
|
+
leaf = root.add_child(Node.create!(:name => "leaf"))
|
39
|
+
Node.roots.should == [root]
|
40
|
+
Node.leaves.should == [leaf]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "3 node collection.create db" do
|
45
|
+
before :each do
|
46
|
+
@root = Node.create! :name => "root"
|
47
|
+
@mid = @root.children.create! :name => "mid"
|
48
|
+
@leaf = @mid.children.create! :name => "leaf"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should create all nodes" do
|
52
|
+
Node.all.should =~ [@root, @mid, @leaf]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should return a root and leaf without middle node" do
|
56
|
+
Node.roots.should == [@root]
|
57
|
+
Node.leaves.should == [@leaf]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should delete leaves" do
|
61
|
+
Node.leaves.destroy_all
|
62
|
+
Node.roots.should == [@root] # untouched
|
63
|
+
Node.leaves.should == [@mid]
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should delete everything if you delete the roots" do
|
67
|
+
Node.roots.destroy_all
|
68
|
+
Node.all.should be_empty
|
69
|
+
Node.roots.should be_empty
|
70
|
+
Node.leaves.should be_empty
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "3 node explicit_create db" do
|
75
|
+
before :each do
|
76
|
+
@root = Node.create!(:name => "root")
|
77
|
+
@mid = @root.add_child(Node.create!(:name => "mid"))
|
78
|
+
@leaf = @mid.add_child(Node.create!(:name => "leaf"))
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should create all nodes" do
|
82
|
+
Node.all.should =~ [@root, @mid, @leaf]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should return a root and leaf without middle node" do
|
86
|
+
Node.roots.should == [@root]
|
87
|
+
Node.leaves.should == [@leaf]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should prevent parental loops from torso" do
|
91
|
+
@mid.children << @root
|
92
|
+
@root.valid?.should be_false
|
93
|
+
@mid.reload.children.should == [@leaf]
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should prevent parental loops from toes" do
|
97
|
+
@leaf.children << @root
|
98
|
+
@root.valid?.should be_false
|
99
|
+
@leaf.reload.children.should be_empty
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should support re-parenting" do
|
103
|
+
@root.children << @leaf
|
104
|
+
Node.leaves.should =~ [@leaf, @mid]
|
105
|
+
end
|
106
|
+
|
107
|
+
it "cleans up hierarchy references for leaves" do
|
108
|
+
@leaf.destroy
|
109
|
+
NodeHierarchy.find_all_by_ancestor_id(@leaf.id).should be_empty
|
110
|
+
NodeHierarchy.find_all_by_descendant_id(@leaf.id).should be_empty
|
111
|
+
end
|
112
|
+
|
113
|
+
it "cleans up hierarchy references" do
|
114
|
+
@mid.destroy
|
115
|
+
NodeHierarchy.find_all_by_ancestor_id(@mid.id).should be_empty
|
116
|
+
NodeHierarchy.find_all_by_descendant_id(@mid.id).should be_empty
|
117
|
+
@root.reload.should be_root
|
118
|
+
root_hiers = @root.ancestor_hierarchies.to_a
|
119
|
+
root_hiers.size.should == 1
|
120
|
+
NodeHierarchy.find_all_by_ancestor_id(@root.id).should == root_hiers
|
121
|
+
NodeHierarchy.find_all_by_descendant_id(@root.id).should == root_hiers
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
it "performs as the readme says it does" do
|
126
|
+
grandparent = Node.create(:name => 'Grandparent')
|
127
|
+
parent = grandparent.children.create(:name => 'Parent')
|
128
|
+
child1 = Node.create(:name => 'First Child', :parent => parent)
|
129
|
+
child2 = Node.new(:name => 'Second Child')
|
130
|
+
parent.children << child2
|
131
|
+
child3 = Node.new(:name => 'Third Child')
|
132
|
+
parent.add_child child3
|
133
|
+
grandparent.self_and_descendants.collect(&:name).should ==
|
134
|
+
["Grandparent", "Parent", "First Child", "Second Child", "Third Child"]
|
135
|
+
child1.ancestry_path.should ==
|
136
|
+
["Grandparent", "Parent", "First Child"]
|
137
|
+
child3.ancestry_path.should ==
|
138
|
+
["Grandparent", "Parent", "Third Child"]
|
139
|
+
d = Node.find_or_create_by_path %w(a b c d)
|
140
|
+
h = Node.find_or_create_by_path %w(e f g h)
|
141
|
+
e = h.root
|
142
|
+
d.add_child(e) # "d.children << e" would work too, of course
|
143
|
+
h.ancestry_path.should == %w(a b c d e f g h)
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
data/spec/support/models.rb
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
require 'uuidtools'
|
2
|
+
|
3
|
+
class Node < ActiveRecord::Base
|
4
|
+
acts_as_tree :dependent => :destroy
|
5
|
+
before_create :generate_uuid
|
6
|
+
attr_accessible :name
|
7
|
+
|
8
|
+
def generate_uuid
|
9
|
+
self.id = UUIDTools::UUID.random_create.to_s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
1
13
|
class Tag < ActiveRecord::Base
|
2
14
|
acts_as_tree :dependent => :destroy, :order => "name"
|
3
15
|
before_destroy :add_destroyed_tag
|
@@ -60,4 +72,4 @@ end
|
|
60
72
|
|
61
73
|
class CuisineType < ActiveRecord::Base
|
62
74
|
acts_as_tree
|
63
|
-
end
|
75
|
+
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.7.
|
4
|
+
version: 3.7.2
|
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: 2013-
|
12
|
+
date: 2013-02-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -171,6 +171,22 @@ dependencies:
|
|
171
171
|
- - ! '>='
|
172
172
|
- !ruby/object:Gem::Version
|
173
173
|
version: '0'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: uuidtools
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
174
190
|
- !ruby/object:Gem::Dependency
|
175
191
|
name: strong_parameters
|
176
192
|
requirement: !ruby/object:Gem::Requirement
|
@@ -207,6 +223,7 @@ files:
|
|
207
223
|
- spec/fixtures/tags.yml
|
208
224
|
- spec/hash_tree_spec.rb
|
209
225
|
- spec/label_spec.rb
|
226
|
+
- spec/node_spec.rb
|
210
227
|
- spec/parallel_spec.rb
|
211
228
|
- spec/spec_helper.rb
|
212
229
|
- spec/support/models.rb
|
@@ -226,7 +243,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
226
243
|
version: '0'
|
227
244
|
segments:
|
228
245
|
- 0
|
229
|
-
hash: -
|
246
|
+
hash: -2260215237559696293
|
230
247
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
231
248
|
none: false
|
232
249
|
requirements:
|
@@ -235,7 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
235
252
|
version: '0'
|
236
253
|
segments:
|
237
254
|
- 0
|
238
|
-
hash: -
|
255
|
+
hash: -2260215237559696293
|
239
256
|
requirements: []
|
240
257
|
rubyforge_project:
|
241
258
|
rubygems_version: 1.8.23
|
@@ -250,6 +267,7 @@ test_files:
|
|
250
267
|
- spec/fixtures/tags.yml
|
251
268
|
- spec/hash_tree_spec.rb
|
252
269
|
- spec/label_spec.rb
|
270
|
+
- spec/node_spec.rb
|
253
271
|
- spec/parallel_spec.rb
|
254
272
|
- spec/spec_helper.rb
|
255
273
|
- spec/support/models.rb
|