arboreal 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +73 -0
- data/lib/arboreal.rb +0 -14
- data/lib/arboreal/active_record_extensions.rb +1 -0
- data/lib/arboreal/class_methods.rb +3 -0
- data/lib/arboreal/version.rb +1 -1
- metadata +6 -2
data/README.rdoc
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
= Arboreal
|
2
|
+
|
3
|
+
Arboreal is yet another extension to ActiveRecord to support tree-shaped
|
4
|
+
data structures.
|
5
|
+
|
6
|
+
Arboreal surfaces relationships within the tree like +children+,
|
7
|
+
+ancestors+, +descendants+, and +siblings+ as scopes, so that additional
|
8
|
+
filtering/pagination can be performed.
|
9
|
+
|
10
|
+
== Getting started
|
11
|
+
|
12
|
+
First, install the "arboreal" gem, and add it to your Rails project's <tt>config/environment.rb</tt>.
|
13
|
+
|
14
|
+
Next, you'll need a migration to add an +ancestry_string+ column, and index thereupon:
|
15
|
+
|
16
|
+
class MakeThingsArboreal < ActiveRecord::Migration
|
17
|
+
|
18
|
+
def self.up
|
19
|
+
add_column "things", "ancestry_string", :string
|
20
|
+
add_index "things", ["ancestry_string"]
|
21
|
+
Thing.rebuild_ancestry
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.down
|
25
|
+
remove_index "things", ["ancestry_string"]
|
26
|
+
remove_column "things", "ancestry_string"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
This assumes that the table concerned already has a +parent_id+ column; if not, add that too (type +integer+, with a supporting index).
|
32
|
+
|
33
|
+
Finally, you can declare you model arboreal:
|
34
|
+
|
35
|
+
class Thing < ActiveRecord::Base
|
36
|
+
|
37
|
+
acts_arboreal
|
38
|
+
|
39
|
+
# .. etc etc ...
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
== Navigating the tree
|
44
|
+
|
45
|
+
Arboreal adds the relationships you'd expect:
|
46
|
+
|
47
|
+
* <tt>parent</tt>
|
48
|
+
* <tt>children</tt>
|
49
|
+
|
50
|
+
In addition, it provides the following handy methods on each tree-node:
|
51
|
+
|
52
|
+
* <tt>ancestors</tt>
|
53
|
+
* <tt>descendants</tt>
|
54
|
+
* <tt>subtree</tt> (the node itself, plus descendants)
|
55
|
+
* <tt>siblings</tt>
|
56
|
+
* <tt>root</tt> (the topmost ancestor)
|
57
|
+
|
58
|
+
The first four return scopes, to which additional filtering, ordering or limits may be applied.
|
59
|
+
|
60
|
+
At the class-level:
|
61
|
+
|
62
|
+
* <tt>roots</tt> is a named-scope returning all the nodes without parents
|
63
|
+
* <tt>rebuild_ancestry</tt> rebuilds the ancestry cache, as described below
|
64
|
+
|
65
|
+
== Rebuilding the ancestry cache
|
66
|
+
|
67
|
+
Internally, Arboreal uses the +ancestry_string+ column to cache the path down the tree to each node (or more correctly, it's parent. This technique - a variant of "path enumeration" or "materialized paths" - allows efficient retrieval of both ancestors and descendants.
|
68
|
+
|
69
|
+
It's conceivable that the computed ancestry-string values may get out of whack, particularly if changes are made directly to the database. If you suspect corruption, you can restore sanity using <tt>rebuild_ancestry</tt>, e.g
|
70
|
+
|
71
|
+
Thing.rebuild_ancestry
|
72
|
+
|
73
|
+
The ancestry rebuild is implemented in SQL to leverage the underlying DBMS, and so is pretty efficient, even on large trees.
|
data/lib/arboreal.rb
CHANGED
@@ -1,17 +1,3 @@
|
|
1
|
-
# Arboreal is yet another extension to ActiveRecord to support tree-shaped
|
2
|
-
# data structures.
|
3
|
-
#
|
4
|
-
# Internally, Arboreal maintains a computed "ancestry_string" column, which
|
5
|
-
# caches the path from the root of a tree to each node, allowing efficient
|
6
|
-
# retrieval of both ancestors and descendants.
|
7
|
-
#
|
8
|
-
# Arboreal surfaces relationships within the tree like +children+,
|
9
|
-
# +ancestors+, +descendants+, and +siblings+ as scopes, so that additional
|
10
|
-
# filtering/pagination can be performed.
|
11
|
-
#
|
12
|
-
module Arboreal
|
13
|
-
end
|
14
|
-
|
15
1
|
require 'arboreal/active_record_extensions'
|
16
2
|
require 'arboreal/class_methods'
|
17
3
|
require 'arboreal/instance_methods'
|
@@ -36,6 +36,7 @@ module Arboreal
|
|
36
36
|
JOIN _arboreals_ AS parent ON parent.id = child.parent_id
|
37
37
|
SET child.ancestry_string = CONCAT(parent.ancestry_string, parent.id, '-')
|
38
38
|
WHERE child.ancestry_string IS NULL
|
39
|
+
AND parent.ancestry_string IS NOT NULL
|
39
40
|
SQL
|
40
41
|
elsif connection.adapter_name == "JDBC" && connection.config[:url] =~ /sqlserver/
|
41
42
|
<<-SQL
|
@@ -44,6 +45,7 @@ module Arboreal
|
|
44
45
|
FROM _arboreals_ AS child
|
45
46
|
JOIN _arboreals_ AS parent ON parent.id = child.parent_id
|
46
47
|
WHERE child.ancestry_string IS NULL
|
48
|
+
AND parent.ancestry_string IS NOT NULL
|
47
49
|
SQL
|
48
50
|
else # SQLite, PostgreSQL, most others (SQL-92)
|
49
51
|
<<-SQL
|
@@ -52,6 +54,7 @@ module Arboreal
|
|
52
54
|
SELECT (parent.ancestry_string || _arboreals_.parent_id || '-')
|
53
55
|
FROM _arboreals_ AS parent
|
54
56
|
WHERE parent.id = _arboreals_.parent_id
|
57
|
+
AND parent.ancestry_string IS NOT NULL
|
55
58
|
)
|
56
59
|
WHERE ancestry_string IS NULL
|
57
60
|
SQL
|
data/lib/arboreal/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arboreal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Williams
|
@@ -47,6 +47,7 @@ executables: []
|
|
47
47
|
extensions: []
|
48
48
|
|
49
49
|
extra_rdoc_files:
|
50
|
+
- README.rdoc
|
50
51
|
- LICENSE
|
51
52
|
files:
|
52
53
|
- lib/arboreal/active_record_extensions.rb
|
@@ -57,6 +58,7 @@ files:
|
|
57
58
|
- spec/arboreal_spec.rb
|
58
59
|
- spec/spec_helper.rb
|
59
60
|
- Rakefile
|
61
|
+
- README.rdoc
|
60
62
|
- LICENSE
|
61
63
|
has_rdoc: true
|
62
64
|
homepage: http://github.com/mdub/arboreal
|
@@ -64,8 +66,10 @@ licenses: []
|
|
64
66
|
|
65
67
|
post_install_message:
|
66
68
|
rdoc_options:
|
67
|
-
- --
|
69
|
+
- --title
|
68
70
|
- Arboreal
|
71
|
+
- --main
|
72
|
+
- README.rdoc
|
69
73
|
require_paths:
|
70
74
|
- lib
|
71
75
|
required_ruby_version: !ruby/object:Gem::Requirement
|