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.
@@ -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.
@@ -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'
@@ -1,6 +1,7 @@
1
1
  module Arboreal
2
2
  module ActiveRecordExtensions
3
3
 
4
+ # Declares that this ActiveRecord::Base model has a tree-like structure.
4
5
  def acts_arboreal
5
6
 
6
7
  belongs_to :parent, :class_name => self.name
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Arboreal
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
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
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
- - --main
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