closure_tree 1.0.0.beta5 → 1.0.0.beta6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +137 -0
- data/Rakefile +3 -9
- data/lib/closure_tree/version.rb +1 -1
- metadata +8 -7
- data/README.rdoc +0 -122
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# Closure Tree
|
2
|
+
|
3
|
+
Closure Tree is a mostly-API-compatible replacement for the
|
4
|
+
acts_as_tree and awesome_nested_set gems, but with much better
|
5
|
+
mutation performance thanks to the Closure Tree storage algorithm.
|
6
|
+
|
7
|
+
See [Bill Karwin](http://karwin.blogspot.com/)'s excellent
|
8
|
+
[Models for hierarchical data presentation](http://www.slideshare.net/billkarwin/models-for-hierarchical-data)
|
9
|
+
for a description of different tree storage algorithms.
|
10
|
+
|
11
|
+
## Setup
|
12
|
+
|
13
|
+
Note that closure_tree is being developed for Rails 3.1.0.rc1
|
14
|
+
|
15
|
+
1. Add this to your Gemfile: ```gem 'closure_tree'```
|
16
|
+
|
17
|
+
2. Run ```bundle install```
|
18
|
+
|
19
|
+
3. Add ```acts_as_tree``` to your hierarchical model(s).
|
20
|
+
|
21
|
+
4. Add a migration to add a ```parent_id``` column to the model you want to act_as_tree.
|
22
|
+
|
23
|
+
Note that if the column is null, the tag will be considered a root node.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
class AddParentIdToTag < ActiveRecord::Migration
|
27
|
+
def change
|
28
|
+
add_column :tag, :parent_id, :integer
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
5. Add a database migration to store the hierarchy for your model. By
|
34
|
+
convention the table name will be the model's table name, followed by
|
35
|
+
"_hierarchy". Note that by calling ```acts_as_tree```, a "virtual model" (in this case, ```TagsHierarchy```) will be added automatically, so you don't need to create it.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class CreateTagHierarchy < ActiveRecord::Migration
|
39
|
+
def change
|
40
|
+
create_table :tags_hierarchy, :id => false do |t|
|
41
|
+
t.integer :ancestor_id, :null => false # ID of the parent/grandparent/great-grandparent/... tag
|
42
|
+
t.integer :descendant_id, :null => false # ID of the target tag
|
43
|
+
t.integer :generations, :null => false # Number of generations between the ancestor and the descendant. Parent/child = 1, for example.
|
44
|
+
end
|
45
|
+
|
46
|
+
# For "all progeny of..." selects:
|
47
|
+
add_index :tags_hierarchy, [:ancestor_id, :descendant_id], :unique => true
|
48
|
+
|
49
|
+
# For "all ancestors of..." selects
|
50
|
+
add_index :tags_hierarchy, [:descendant_id]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
6. Run ```rake db:migrate```
|
56
|
+
|
57
|
+
7. If you're migrating away from another system where your model already has a
|
58
|
+
```parent_id``` column, run ```Tag.rebuild!``` and the
|
59
|
+
..._hierarchy table will be truncated and rebuilt.
|
60
|
+
|
61
|
+
If you're starting from scratch you don't need to call ```rebuild!```.
|
62
|
+
|
63
|
+
## Usage
|
64
|
+
|
65
|
+
### Creation
|
66
|
+
|
67
|
+
Create a root node:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
grandparent = Tag.create!(:name => 'Grandparent')
|
71
|
+
```
|
72
|
+
|
73
|
+
There are two equivalent ways to add children. Either use the ```add_child``` method:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
parent = Tag.create!(:name => 'Parent')
|
77
|
+
grandparent.add_child parent
|
78
|
+
```
|
79
|
+
|
80
|
+
Or append to the ```children``` collection:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
child = Tag.create!(:name => 'Child')
|
84
|
+
parent.children << child
|
85
|
+
```
|
86
|
+
|
87
|
+
Then:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
puts grandparent.self_and_descendants.collect{ |t| t.name }.join(" > ")
|
91
|
+
"grandparent > parent > child"
|
92
|
+
|
93
|
+
child.ancestry_path
|
94
|
+
["grandparent", "parent", "child"]
|
95
|
+
```
|
96
|
+
|
97
|
+
### <code>find_or_create_by_path</code>
|
98
|
+
|
99
|
+
We can do all the node creation and add_child calls from the prior section with one method call:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
child = Tag.find_or_create_by_path "grandparent", "parent", "child"
|
103
|
+
```
|
104
|
+
|
105
|
+
You can ```find``` as well as ```find_or_create``` by "ancestry paths". Ancestry paths may be built using any column in your model. The default column is ```name```, which can be changed with the :name_column option provided to ```acts_as_tree```.
|
106
|
+
|
107
|
+
Note that the other columns will be null if nodes are created, other than auto-generated columns like ID and created_at timestamp. Only the specified column will receive the path element value.
|
108
|
+
|
109
|
+
## Accessing Data
|
110
|
+
|
111
|
+
### Class methods
|
112
|
+
|
113
|
+
* ``` Tag.root``` returns an arbitrary root node
|
114
|
+
* ``` Tag.roots``` returns all root nodes
|
115
|
+
* ``` Tag.leaves``` returns all leaf nodes
|
116
|
+
|
117
|
+
### Instance methods
|
118
|
+
|
119
|
+
* ``` tag.root``` returns the root for this node
|
120
|
+
* ``` tag.root?``` returns true if this is a root node
|
121
|
+
* ``` tag.child?``` returns true if this is a child node. It has a parent.
|
122
|
+
* ``` tag.leaf?``` returns true if this is a leaf node. It has no children.
|
123
|
+
* ``` tag.leaves``` returns an array of all the nodes in self_and_descendants that are leaves.
|
124
|
+
* ``` tag.level``` returns the level, or "generation", for this node in the tree. A root node = 0
|
125
|
+
* ``` tag.parent``` returns the node's immediate parent
|
126
|
+
* ``` tag.children``` returns an array of immediate children (just those in the next level).
|
127
|
+
* ``` tag.ancestors``` returns an array of all parents, parents' parents, etc, excluding self.
|
128
|
+
* ``` tag.self_and_ancestors``` returns an array of all parents, parents' parents, etc, including self.
|
129
|
+
* ``` tag.siblings``` returns an array of brothers and sisters (all at that level), excluding self.
|
130
|
+
* ``` tag.self_and_siblings``` returns an array of brothers and sisters (all at that level), including self.
|
131
|
+
* ``` tag.descendants``` returns an array of all children, childrens' children, etc., excluding self.
|
132
|
+
* ``` tag.self_and_descendants``` returns an array of all children, childrens' children, etc., including self.
|
133
|
+
|
134
|
+
## Thanks to
|
135
|
+
|
136
|
+
* https://github.com/collectiveidea/awesome_nested_set
|
137
|
+
* https://github.com/patshaughnessy/class_factory
|
data/Rakefile
CHANGED
@@ -6,18 +6,12 @@ end
|
|
6
6
|
|
7
7
|
Bundler::GemHelper.install_tasks
|
8
8
|
|
9
|
+
require 'yard'
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
RDoc::Task.new do |rdoc|
|
13
|
-
rdoc.rdoc_dir = 'rdoc'
|
14
|
-
rdoc.title = 'ClosureTree'
|
15
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
16
|
-
rdoc.rdoc_files.include('README.rdoc')
|
17
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
11
|
+
YARD::Rake::YardocTask.new do |t|
|
12
|
+
t.files = ['lib/**/*.rb', 'README.md']
|
18
13
|
end
|
19
14
|
|
20
|
-
|
21
15
|
require 'rake/testtask'
|
22
16
|
|
23
17
|
Rake::TestTask.new(:test) do |t|
|
data/lib/closure_tree/version.rb
CHANGED
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: closure_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta6
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
|
+
- matthew-github@mceachen.org
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2011-
|
12
|
+
date: 2011-07-02 00:00:00.000000000 -07:00
|
12
13
|
default_executable:
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: activerecord
|
16
|
-
requirement: &
|
17
|
+
requirement: &2162178320 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ! '>='
|
@@ -21,7 +22,7 @@ dependencies:
|
|
21
22
|
version: 3.0.0
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
+
version_requirements: *2162178320
|
25
26
|
description: ! " A mostly-API-compatible replacement for the acts_as_tree and awesome_nested_set
|
26
27
|
gems,\n but with much better mutation performance thanks to the Closure Tree storage
|
27
28
|
algorithm\n"
|
@@ -36,7 +37,7 @@ files:
|
|
36
37
|
- lib/tasks/closure_tree_tasks.rake
|
37
38
|
- MIT-LICENSE
|
38
39
|
- Rakefile
|
39
|
-
- README.
|
40
|
+
- README.md
|
40
41
|
- test/dummy/Rakefile
|
41
42
|
- test/dummy/app/models/.gitkeep
|
42
43
|
- test/dummy/app/models/tag.rb
|
@@ -71,7 +72,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
72
|
version: '0'
|
72
73
|
segments:
|
73
74
|
- 0
|
74
|
-
hash:
|
75
|
+
hash: 1847009308530126687
|
75
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
77
|
none: false
|
77
78
|
requirements:
|
data/README.rdoc
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
= Closure Tree
|
2
|
-
|
3
|
-
Closure Tree is a mostly-API-compatible replacement for the
|
4
|
-
acts_as_tree and awesome_nested_set gems, but with much better
|
5
|
-
mutation performance thanks to the Closure Tree storage algorithm.
|
6
|
-
|
7
|
-
See {Bill Karwin}[http://karwin.blogspot.com/]'s excellent
|
8
|
-
{Models for hierarchical data presentation}[http://www.slideshare.net/billkarwin/models-for-hierarchical-data]
|
9
|
-
for a description of different tree storage algorithms.
|
10
|
-
|
11
|
-
== Setup
|
12
|
-
|
13
|
-
Note that closure_tree is being developed for Rails 3.1.0.rc1
|
14
|
-
|
15
|
-
1. Add this to your Gemfile: <code>gem 'closure_tree'</code>
|
16
|
-
|
17
|
-
2. Run <code>bundle install</code>
|
18
|
-
|
19
|
-
3. Add <code>acts_as_tree</code> to your hierarchical model(s).
|
20
|
-
|
21
|
-
4. Add a migration to add a <code>parent_id</code> column to the model you want to act_as_tree.
|
22
|
-
|
23
|
-
Note that if the column is null, the tag will be considered a root node.
|
24
|
-
|
25
|
-
class AddParentIdToTag < ActiveRecord::Migration
|
26
|
-
def change
|
27
|
-
add_column :tag, :parent_id, :integer
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
5. Add a database migration to store the hierarchy for your model. By
|
32
|
-
convention the table name will be the model's table name, followed by
|
33
|
-
"_hierarchy". Note that by calling <code>acts_as_tree</code>, a "virtual model" (in this case, <code>TagsHierarchy</code>) will be added automatically, so you don't need to create it.
|
34
|
-
|
35
|
-
class CreateTagHierarchy < ActiveRecord::Migration
|
36
|
-
def change
|
37
|
-
create_table :tags_hierarchy, :id => false do |t|
|
38
|
-
t.integer :ancestor_id, :null => false # ID of the parent/grandparent/great-grandparent/... tag
|
39
|
-
t.integer :descendant_id, :null => false # ID of the target tag
|
40
|
-
t.integer :generations, :null => false # Number of generations between the ancestor and the descendant. Parent/child = 1, for example.
|
41
|
-
end
|
42
|
-
|
43
|
-
# For "all progeny of..." selects:
|
44
|
-
add_index :tags_hierarchy, [:ancestor_id, :descendant_id], :unique => true
|
45
|
-
|
46
|
-
# For "all ancestors of..." selects
|
47
|
-
add_index :tags_hierarchy, [:descendant_id]
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
6. Run <code>rake db:migrate</code>
|
52
|
-
|
53
|
-
7. If you're migrating away from another system where your model already has a
|
54
|
-
<code>parent_id</code> column, run <code>Tag.rebuild!</code> and the
|
55
|
-
..._hierarchy table will be truncated and rebuilt.
|
56
|
-
|
57
|
-
If you're starting from scratch you don't need to call <code>rebuild!</code>.
|
58
|
-
|
59
|
-
== Usage
|
60
|
-
|
61
|
-
=== Creation
|
62
|
-
|
63
|
-
Create a root node:
|
64
|
-
|
65
|
-
grandparent = Tag.create!(:name => 'Grandparent')
|
66
|
-
|
67
|
-
There are two equivalent ways to add children. Either use the <code>add_child</code> method:
|
68
|
-
|
69
|
-
parent = Tag.create!(:name => 'Parent')
|
70
|
-
grandparent.add_child parent
|
71
|
-
|
72
|
-
Or append to the <code>children</code> collection:
|
73
|
-
|
74
|
-
child = Tag.create!(:name => 'Child')
|
75
|
-
parent.children << child
|
76
|
-
|
77
|
-
Then:
|
78
|
-
|
79
|
-
puts grandparent.self_and_descendants.collect{ |t| t.name }.join(" > ")
|
80
|
-
"grandparent > parent > child"
|
81
|
-
|
82
|
-
child.ancestry_path
|
83
|
-
["grandparent", "parent", "child"]
|
84
|
-
|
85
|
-
=== <code>find_or_create_by_path</code>
|
86
|
-
|
87
|
-
We can do all the node creation and add_child calls from the prior section with one method call:
|
88
|
-
child = Tag.find_or_create_by_path "grandparent", "parent", "child"
|
89
|
-
|
90
|
-
You can <code>find</code> as well as <code>find_or_create</code> by "ancestry paths". Ancestry paths may be built using any column in your model. The default column is <code>name</code>, which can be changed with the :name_column option provided to <code>acts_as_tree</code>.
|
91
|
-
|
92
|
-
Note that the other columns will be null if nodes are created, other than auto-generated columns like ID and created_at timestamp. Only the specified column will receive the path element value.
|
93
|
-
|
94
|
-
== Accessing Data
|
95
|
-
|
96
|
-
=== Class methods
|
97
|
-
|
98
|
-
[Tag.root] returns an arbitrary root node
|
99
|
-
[Tag.roots] returns all root nodes
|
100
|
-
[Tag.leaves] returns all leaf nodes
|
101
|
-
|
102
|
-
=== Instance methods
|
103
|
-
|
104
|
-
[tag.root] returns the root for this node
|
105
|
-
[tag.root?] returns true if this is a root node
|
106
|
-
[tag.child?] returns true if this is a child node. It has a parent.
|
107
|
-
[tag.leaf?] returns true if this is a leaf node. It has no children.
|
108
|
-
[tag.leaves] returns an array of all the nodes in self_and_descendants that are leaves.
|
109
|
-
[tag.level] returns the level, or "generation", for this node in the tree. A root node = 0
|
110
|
-
[tag.parent] returns the node's immediate parent
|
111
|
-
[tag.children] returns an array of immediate children (just those in the next level).
|
112
|
-
[tag.ancestors] returns an array of all parents, parents' parents, etc, excluding self.
|
113
|
-
[tag.self_and_ancestors] returns an array of all parents, parents' parents, etc, including self.
|
114
|
-
[tag.siblings] returns an array of brothers and sisters (all at that level), excluding self.
|
115
|
-
[tag.self_and_siblings] returns an array of brothers and sisters (all at that level), including self.
|
116
|
-
[tag.descendants] returns an array of all children, childrens' children, etc., excluding self.
|
117
|
-
[tag.self_and_descendants] returns an array of all children, childrens' children, etc., including self.
|
118
|
-
|
119
|
-
== Thanks to
|
120
|
-
|
121
|
-
* https://github.com/collectiveidea/awesome_nested_set
|
122
|
-
* https://github.com/patshaughnessy/class_factory
|