closure_tree 1.0.0.beta5 → 1.0.0.beta6
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 +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
|