arboreal 0.0.4 → 0.0.5
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.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
|