dagnabit 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.autotest +5 -0
  2. data/.document +5 -0
  3. data/.gitignore +6 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +186 -0
  6. data/Rakefile +69 -0
  7. data/VERSION.yml +5 -0
  8. data/bin/dagnabit-test +53 -0
  9. data/dagnabit.gemspec +124 -0
  10. data/init.rb +1 -0
  11. data/lib/dagnabit.rb +17 -0
  12. data/lib/dagnabit/activation.rb +60 -0
  13. data/lib/dagnabit/link/associations.rb +18 -0
  14. data/lib/dagnabit/link/class_methods.rb +43 -0
  15. data/lib/dagnabit/link/configuration.rb +40 -0
  16. data/lib/dagnabit/link/cycle_prevention.rb +31 -0
  17. data/lib/dagnabit/link/named_scopes.rb +65 -0
  18. data/lib/dagnabit/link/transitive_closure_link_model.rb +86 -0
  19. data/lib/dagnabit/link/transitive_closure_recalculation.rb +17 -0
  20. data/lib/dagnabit/link/transitive_closure_recalculation/on_create.rb +104 -0
  21. data/lib/dagnabit/link/transitive_closure_recalculation/on_destroy.rb +125 -0
  22. data/lib/dagnabit/link/transitive_closure_recalculation/on_update.rb +13 -0
  23. data/lib/dagnabit/link/transitive_closure_recalculation/utilities.rb +56 -0
  24. data/lib/dagnabit/link/validations.rb +26 -0
  25. data/lib/dagnabit/node/associations.rb +84 -0
  26. data/lib/dagnabit/node/class_methods.rb +74 -0
  27. data/lib/dagnabit/node/configuration.rb +26 -0
  28. data/lib/dagnabit/node/neighbors.rb +73 -0
  29. data/test/connections/native_postgresql/connection.rb +17 -0
  30. data/test/connections/native_sqlite3/connection.rb +24 -0
  31. data/test/dagnabit/link/test_associations.rb +61 -0
  32. data/test/dagnabit/link/test_class_methods.rb +102 -0
  33. data/test/dagnabit/link/test_configuration.rb +38 -0
  34. data/test/dagnabit/link/test_cycle_prevention.rb +64 -0
  35. data/test/dagnabit/link/test_named_scopes.rb +32 -0
  36. data/test/dagnabit/link/test_transitive_closure_link_model.rb +69 -0
  37. data/test/dagnabit/link/test_transitive_closure_recalculation.rb +139 -0
  38. data/test/dagnabit/link/test_validations.rb +39 -0
  39. data/test/dagnabit/node/test_associations.rb +147 -0
  40. data/test/dagnabit/node/test_class_methods.rb +49 -0
  41. data/test/dagnabit/node/test_configuration.rb +29 -0
  42. data/test/dagnabit/node/test_neighbors.rb +91 -0
  43. data/test/helper.rb +27 -0
  44. data/test/models/beta_node.rb +3 -0
  45. data/test/models/custom_data_link.rb +4 -0
  46. data/test/models/customized_link.rb +7 -0
  47. data/test/models/customized_link_node.rb +4 -0
  48. data/test/models/link.rb +4 -0
  49. data/test/models/node.rb +3 -0
  50. data/test/schema/schema.rb +51 -0
  51. metadata +165 -0
@@ -0,0 +1,17 @@
1
+ print "Using native PostgreSQL\n"
2
+
3
+ require 'logger'
4
+
5
+ ActiveRecord::Base.logger = Logger.new("debug.log")
6
+
7
+ ActiveRecord::Base.configurations = {
8
+ 'ActiveRecord::Base' => {
9
+ :adapter => 'postgresql',
10
+ :database => 'dagnabit_test',
11
+ :username => 'dagnabit_test',
12
+ :password => 'dagnabit_test',
13
+ :hostname => 'localhost'
14
+ }
15
+ };
16
+
17
+ ActiveRecord::Base.establish_connection('ActiveRecord::Base')
@@ -0,0 +1,24 @@
1
+ print "Using native SQLite3\n"
2
+
3
+ require 'logger'
4
+
5
+ # The SQLite3 Ruby driver can be present as either a gem or directly present in
6
+ # the Ruby library load path. This load method addresses these two situations.
7
+ begin
8
+ require 'sqlite3'
9
+ rescue LoadError
10
+ require 'rubygems'
11
+ gem 'sqlite3-ruby'
12
+ require 'sqlite3'
13
+ end
14
+
15
+ ActiveRecord::Base.logger = Logger.new("debug.log")
16
+
17
+ class SqliteError < StandardError
18
+ end
19
+
20
+ ActiveRecord::Base.configurations = {
21
+ 'ActiveRecord::Base' => { :adapter => 'sqlite3', :database => ':memory:' }
22
+ };
23
+
24
+ ActiveRecord::Base.establish_connection('ActiveRecord::Base')
@@ -0,0 +1,61 @@
1
+ require 'helper'
2
+
3
+ module Dagnabit
4
+ module Link
5
+ class TestAssociations < ActiveRecord::TestCase
6
+ class Node < ActiveRecord::Base
7
+ set_table_name 'nodes'
8
+ end
9
+
10
+ class Link < ActiveRecord::Base
11
+ set_table_name 'edges'
12
+ extend Dagnabit::Link::Configuration
13
+ configure_acts_as_dag_link({})
14
+ extend Dagnabit::Link::Associations
15
+ end
16
+
17
+ class CustomLink < ActiveRecord::Base
18
+ set_table_name 'other_name_edges'
19
+
20
+ extend Dagnabit::Link::Configuration
21
+ configure_acts_as_dag_link(:ancestor_id_column => 'the_ancestor_id',
22
+ :descendant_id_column => 'the_descendant_id',
23
+ :transitive_closure_table_name => 'my_transitive_closure')
24
+
25
+ extend Dagnabit::Link::Associations
26
+ end
27
+
28
+ def setup
29
+ @node = Node.new
30
+ @link = Link.new
31
+ @custom_link = CustomLink.new
32
+ end
33
+
34
+ should 'add an ancestor association proxy' do
35
+ @link.ancestor = @node
36
+ @link.save
37
+ assert_equal @node, @link.reload.ancestor
38
+ end
39
+
40
+ should 'add a descendant association proxy' do
41
+ @link.descendant = @node
42
+ @link.save
43
+ assert_equal @node, @link.reload.descendant
44
+ end
45
+
46
+ should 'permit customization of the ancestor association foreign key' do
47
+ @custom_link.ancestor = @node
48
+ @custom_link.save
49
+ @custom_link = CustomLink.find(@custom_link.id)
50
+ assert_equal @node, @custom_link.ancestor
51
+ end
52
+
53
+ should 'permit customization of the descendant association foreign key' do
54
+ @custom_link.descendant = @node
55
+ @custom_link.save
56
+ @custom_link = CustomLink.find(@custom_link.id)
57
+ assert_equal @node, @custom_link.descendant
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,102 @@
1
+ require 'helper'
2
+
3
+ module Dagnabit
4
+ module Link
5
+ class TestClassMethods < ActiveRecord::TestCase
6
+ should 'include a method to build edges' do
7
+ n1 = ::Node.new
8
+ n2 = ::Node.new
9
+
10
+ link = ::Link.build_edge(n1, n2)
11
+ assert_equal n1, link.ancestor
12
+ assert_equal n2, link.descendant
13
+ end
14
+
15
+ should 'permit custom data to be included on edges' do
16
+ n1 = ::Node.new
17
+ n2 = ::Node.new
18
+
19
+ link = CustomDataLink.build_edge(n1, n2, :data => 'foobar')
20
+ assert_equal 'foobar', link.data
21
+ end
22
+
23
+ context 'connect' do
24
+ should 'connect two nodes' do
25
+ n1 = ::Node.new
26
+ n2 = ::Node.new
27
+
28
+ ::Link.connect(n1, n2)
29
+ link = ::Link.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n2.id })
30
+
31
+ assert_not_nil link
32
+ assert_equal n1, link.ancestor
33
+ assert_equal n2, link.descendant
34
+ end
35
+
36
+ should 'permit custom data to be included on edges' do
37
+ n1 = ::Node.new
38
+ n2 = ::Node.new
39
+
40
+ CustomDataLink.connect(n1, n2, :data => 'foobar')
41
+ link = CustomDataLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n2.id })
42
+
43
+ assert_equal 'foobar', link.data
44
+ end
45
+
46
+ should 'return false if connection fails due to node invalidity' do
47
+ n1 = ::Node.new
48
+ n2 = ::Node.new
49
+
50
+ n1.stubs(:valid?).returns(false)
51
+
52
+ result = ::Link.connect(n1, n2)
53
+ assert_equal false, result
54
+ end
55
+
56
+ should 'not save endpoints if connection fails due to node invalidity' do
57
+ n1 = ::Node.new
58
+ n2 = ::Node.new
59
+
60
+ n1.stubs(:valid?).returns(false)
61
+
62
+ ::Link.connect(n1, n2)
63
+
64
+ assert n1.new_record?
65
+ assert n2.new_record?
66
+ end
67
+ end
68
+
69
+ context 'path?' do
70
+ should 'return whether or not a path exists between two nodes' do
71
+ n1 = ::Node.new
72
+ n2 = ::Node.new
73
+ n3 = ::Node.new
74
+ n4 = ::Node.new
75
+
76
+ ::Link.connect(n1, n2)
77
+ ::Link.connect(n2, n3)
78
+
79
+ assert ::Link.path?(n1, n2)
80
+ assert ::Link.path?(n1, n3)
81
+ assert !::Link.path?(n1, n4)
82
+ end
83
+ end
84
+
85
+ context 'paths' do
86
+ should 'return all paths between two nodes' do
87
+ n1 = ::Node.new
88
+ n2 = ::Node.new
89
+ n3 = ::Node.new
90
+
91
+ ::Link.connect(n1, n2)
92
+ ::Link.connect(n2, n3)
93
+
94
+ paths = ::Link.paths(n1, n3)
95
+ assert_equal 1, paths.length
96
+ assert_equal n1, paths.first.ancestor
97
+ assert_equal n3, paths.first.descendant
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,38 @@
1
+ require 'helper'
2
+
3
+ module Dagnabit
4
+ module Link
5
+ class TestConfiguration < ActiveRecord::TestCase
6
+ should 'have default and settable names for transitive closure model and column names' do
7
+ default_model = Class.new(ActiveRecord::Base) do
8
+ set_table_name 'edges'
9
+
10
+ acts_as_dag_link
11
+ end
12
+
13
+ custom_model = Class.new(ActiveRecord::Base) do
14
+ set_table_name 'other_name_edges'
15
+
16
+ acts_as_dag_link :ancestor_id_column => 'the_ancestor_id',
17
+ :descendant_id_column => 'the_descendant_id',
18
+ :transitive_closure_table_name => 'my_transitive_closure',
19
+ :transitive_closure_class_name => 'MyTransitiveClosureLink'
20
+ end
21
+
22
+ assert_equal 'ancestor_id', default_model.ancestor_id_column
23
+ assert_equal 'descendant_id', default_model.descendant_id_column
24
+ assert_equal default_model.connection.quote_table_name('edges_transitive_closure_tuples'), default_model.transitive_closure_table_name
25
+ assert_equal 'TransitiveClosureLink', default_model.transitive_closure_class_name
26
+
27
+ assert_equal 'the_ancestor_id', custom_model.ancestor_id_column
28
+ assert_equal 'the_descendant_id', custom_model.descendant_id_column
29
+ assert_equal custom_model.connection.quote_table_name('my_transitive_closure'), custom_model.transitive_closure_table_name
30
+ assert_equal 'MyTransitiveClosureLink', custom_model.transitive_closure_class_name
31
+ end
32
+
33
+ should 'not have its configuration accessors on ActiveRecord::Base' do
34
+ assert !ActiveRecord::Base.respond_to?(:ancestor_id_column)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,64 @@
1
+ require 'helper'
2
+
3
+ module Dagnabit
4
+ module Link
5
+ class TestCyclePrevention < ActiveRecord::TestCase
6
+ should 'prevent simple cycles from being saved' do
7
+ n1 = ::Node.create
8
+ n2 = ::Node.create
9
+
10
+ l1 = ::Link.create(:ancestor => n1, :descendant => n2)
11
+ l2 = ::Link.create(:ancestor => n2, :descendant => n1)
12
+
13
+ assert l2.new_record?
14
+ end
15
+
16
+ should 'prevent self-referential nodes' do
17
+ n1 = ::Node.create
18
+
19
+ l1 = ::Link.create(:ancestor => n1, :descendant => n1)
20
+ assert l1.new_record?
21
+ end
22
+
23
+ should 'prevent cycles from being saved' do
24
+ n1 = ::Node.create
25
+ n2 = ::Node.create
26
+ n3 = ::Node.create
27
+ n4 = ::Node.create
28
+
29
+ ::Link.create(:ancestor => n1, :descendant => n2)
30
+ ::Link.create(:ancestor => n2, :descendant => n3)
31
+ ::Link.create(:ancestor => n3, :descendant => n4)
32
+ l = ::Link.create(:ancestor => n4, :descendant => n2)
33
+
34
+ assert l.new_record?
35
+ end
36
+
37
+ should 'not prevent links from being saved multiple times in a row' do
38
+ n1 = ::Node.create
39
+ n2 = ::Node.create
40
+
41
+ l = ::Link.new(:ancestor => n1, :descendant => n2)
42
+
43
+ assert l.save
44
+ assert l.save, 'should have been able to save twice'
45
+ end
46
+
47
+ should 'prevent cycles from being saved in customized links' do
48
+ n1 = ::Node.create
49
+ n2 = ::Node.create
50
+ n3 = ::Node.create
51
+
52
+ ::CustomizedLink.create(:ancestor => n1, :descendant => n2)
53
+ ::CustomizedLink.create(:ancestor => n1, :descendant => n3)
54
+ l3 = ::CustomizedLink.create(:ancestor => n3, :descendant => n1)
55
+
56
+ assert l3.new_record?
57
+ end
58
+
59
+ should 'not execute on links that have no ancestor or descendant' do
60
+ assert_nothing_raised { ::Link.create }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,32 @@
1
+ require 'helper'
2
+
3
+ module Dagnabit
4
+ module Link
5
+ class TestNamedScopes < ActiveRecord::TestCase
6
+ class OtherNode < ActiveRecord::Base
7
+ set_table_name 'nodes'
8
+ end
9
+
10
+ def setup
11
+ @n1 = ::Node.new
12
+ @n2 = OtherNode.new
13
+ @n3 = ::Node.new
14
+
15
+ @l1 = ::Link.build_edge(@n1, @n2)
16
+ @l2 = ::Link.build_edge(@n2, @n3)
17
+ @l1.save
18
+ @l2.save
19
+ end
20
+
21
+ should 'scope links by ancestor type' do
22
+ links = ::Link.ancestor_type(OtherNode.name)
23
+ assert_equal [@l2], links
24
+ end
25
+
26
+ should 'scope links by descendant type' do
27
+ links = ::Link.descendant_type(OtherNode.name)
28
+ assert_equal [@l1], links
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,69 @@
1
+ require 'helper'
2
+
3
+ module Dagnabit
4
+ module Link
5
+ class TestTransitiveClosureLinkModel < ActiveRecord::TestCase
6
+ def setup
7
+ @model = ::Link::TransitiveClosureLink.new
8
+ @model.class.set_table_name 'edges_transitive_closure_tuples'
9
+ @node = ::Node.new
10
+ end
11
+
12
+ should 'belong to an ancestor' do
13
+ @model.ancestor = @node
14
+ @model.save
15
+
16
+ @model = ::Link::TransitiveClosureLink.find(@model.id)
17
+ assert_equal @node, @model.ancestor
18
+ end
19
+
20
+ should 'belong to a descendant' do
21
+ @model.descendant = @node
22
+ @model.save
23
+
24
+ @model = ::Link::TransitiveClosureLink.find(@model.id)
25
+ assert_equal @node, @model.descendant
26
+ end
27
+
28
+ # TODO: wrap tests in transaction
29
+ should 'support find(:all) queries including associations' do
30
+ ::Link::TransitiveClosureLink.delete_all
31
+
32
+ n1 = ::Node.new
33
+ n2 = ::Node.new
34
+ ::Link::TransitiveClosureLink.create(:ancestor => n1, :descendant => n2)
35
+
36
+ links = ::Link::TransitiveClosureLink.find(:all, :include => :ancestor)
37
+ assert_equal 1, links.length
38
+ end
39
+
40
+ should 'support scoping by ancestor type' do
41
+ ::Link::TransitiveClosureLink.delete_all
42
+
43
+ n1 = ::BetaNode.new
44
+ n2 = ::Node.new
45
+
46
+ ::Link::TransitiveClosureLink.create(:ancestor => n1, :descendant => n2)
47
+
48
+ links = ::Link::TransitiveClosureLink.ancestor_type(::BetaNode.name)
49
+
50
+ assert_equal 1, links.length
51
+ assert_equal n1, links.first.ancestor
52
+ end
53
+
54
+ should 'support scoping by descendant type' do
55
+ ::Link::TransitiveClosureLink.delete_all
56
+
57
+ n1 = ::Node.new
58
+ n2 = ::BetaNode.new
59
+
60
+ ::Link::TransitiveClosureLink.create(:ancestor => n1, :descendant => n2)
61
+
62
+ links = ::Link::TransitiveClosureLink.descendant_type(::BetaNode.name)
63
+
64
+ assert_equal 1, links.length
65
+ assert_equal n2, links.first.descendant
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,139 @@
1
+ require 'helper'
2
+
3
+ module Dagnabit
4
+ module Link
5
+ class TestTransitiveClosureRecalculation < ActiveRecord::TestCase
6
+ TransitiveClosureLink = ::Link::TransitiveClosureLink
7
+ CustomTransitiveClosureLink = CustomizedLink::TransitiveClosureLink
8
+ TransitiveClosureCustomDataLink = CustomDataLink::TransitiveClosureLink
9
+
10
+ should 'recalculate transitive closure on create' do
11
+ n1 = ::Node.create
12
+ n2 = ::Node.create
13
+ n3 = ::Node.create
14
+
15
+ ::Link.create(:ancestor => n1, :descendant => n2)
16
+ ::Link.create(:ancestor => n2, :descendant => n3)
17
+
18
+ tc = TransitiveClosureLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n3.id })
19
+ assert_not_nil tc, 'expected to find path from n1 to n3'
20
+ end
21
+
22
+ should 'transfer custom data attributes to transitive closure' do
23
+ n1 = ::Node.create
24
+ n2 = ::Node.create
25
+ n3 = ::Node.create
26
+
27
+ CustomDataLink.create(:ancestor => n1, :descendant => n2, :data => 'foo')
28
+ CustomDataLink.create(:ancestor => n2, :descendant => n3, :data => 'bar')
29
+
30
+ tc1 = TransitiveClosureCustomDataLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n2.id })
31
+ tc2 = TransitiveClosureCustomDataLink.find(:first, :conditions => { :ancestor_id => n2.id, :descendant_id => n3.id })
32
+ tc3 = TransitiveClosureCustomDataLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n3.id })
33
+
34
+ assert_equal 'foo', tc1.data, 'expected to find custom data attribute on n1->n2 edge'
35
+ assert_equal 'bar', tc2.data, 'expected to find custom data attribute on n2->n3 edge'
36
+ assert_not_nil tc3, 'expected to find path from n1 to n3'
37
+ assert_nil tc3.data, 'expected to find no custom data attribute on n1->n3 path'
38
+ end
39
+
40
+ should 'recalculate transitive closure on destroy' do
41
+ n1 = ::Node.create
42
+ n2 = ::Node.create
43
+ n3 = ::Node.create
44
+
45
+ l1 = ::Link.create(:ancestor => n1, :descendant => n2)
46
+ l2 = ::Link.create(:ancestor => n2, :descendant => n3)
47
+
48
+ l2.destroy
49
+
50
+ tc = TransitiveClosureLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n2.id })
51
+ assert_not_nil tc, 'expected to find path from n1 to n2'
52
+
53
+ tc = TransitiveClosureLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n3.id })
54
+ assert_nil tc, 'expected to not find path from n1 to n3'
55
+ end
56
+
57
+ should 'include edges in the graph when reconstructing the transitive closure on destroy' do
58
+ n1 = ::Node.create
59
+ n2 = ::Node.create
60
+ n3 = ::Node.create
61
+
62
+ l1 = ::Link.create(:ancestor => n1, :descendant => n2)
63
+ l2 = ::Link.create(:ancestor => n2, :descendant => n3)
64
+ l3 = ::Link.create(:ancestor => n1, :descendant => n3)
65
+
66
+ l1.destroy
67
+ l2.destroy
68
+
69
+ tc = TransitiveClosureLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n3.id })
70
+ assert_not_nil tc, 'expected to find path from n1 to n3'
71
+ end
72
+
73
+ should 'recalculate transitive closure on update' do
74
+ n1 = ::Node.create
75
+ n2 = ::Node.create
76
+ n3 = ::Node.create
77
+ n4 = ::Node.create
78
+
79
+ l1 = ::Link.create(:ancestor => n1, :descendant => n2)
80
+ l2 = ::Link.create(:ancestor => n2, :descendant => n3)
81
+
82
+ l2.update_attribute(:descendant, n4)
83
+
84
+ tc = TransitiveClosureLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n4.id })
85
+ assert_not_nil tc, 'expected to find path from n1 to n4'
86
+
87
+ tc = TransitiveClosureLink.find(:first, :conditions => { :ancestor_id => n1.id, :descendant_id => n3.id })
88
+ assert_nil tc, 'expected to not find path from n1 to n3'
89
+ end
90
+
91
+ should 'recalculate transitive closure on create using custom-configured link' do
92
+ n1 = ::Node.create
93
+ n2 = ::Node.create
94
+ n3 = ::Node.create
95
+
96
+ CustomizedLink.create(:ancestor => n1, :descendant => n2)
97
+ CustomizedLink.create(:ancestor => n2, :descendant => n3)
98
+
99
+ tc = CustomTransitiveClosureLink.find(:first, :conditions => { :the_ancestor_id => n1.id, :the_descendant_id => n3.id })
100
+ assert_not_nil tc, 'expected to find path from n1 to n3'
101
+ end
102
+
103
+ should 'recalculate transitive closure on destroy using custom-configured link' do
104
+ n1 = ::Node.create
105
+ n2 = ::Node.create
106
+ n3 = ::Node.create
107
+
108
+ l1 = CustomizedLink.create(:ancestor => n1, :descendant => n2)
109
+ l2 = CustomizedLink.create(:ancestor => n2, :descendant => n3)
110
+
111
+ l2.destroy
112
+
113
+ tc = CustomTransitiveClosureLink.find(:first, :conditions => { :the_ancestor_id => n1.id, :the_descendant_id => n2.id })
114
+ assert_not_nil tc, 'expected to find path from n1 to n2'
115
+
116
+ tc = CustomTransitiveClosureLink.find(:first, :conditions => { :the_ancestor_id => n1.id, :the_descendant_id => n3.id })
117
+ assert_nil tc, 'expected to not find path from n1 to n3'
118
+ end
119
+
120
+ should 'recalculate transitive closure on update using custom-configured link' do
121
+ n1 = ::Node.create
122
+ n2 = ::Node.create
123
+ n3 = ::Node.create
124
+ n4 = ::Node.create
125
+
126
+ l1 = CustomizedLink.create(:ancestor => n1, :descendant => n2)
127
+ l2 = CustomizedLink.create(:ancestor => n2, :descendant => n3)
128
+
129
+ l2.update_attribute(:descendant, n4)
130
+
131
+ tc = CustomTransitiveClosureLink.find(:first, :conditions => { :the_ancestor_id => n1.id, :the_descendant_id => n4.id })
132
+ assert_not_nil tc, 'expected to find path from n1 to n4'
133
+
134
+ tc = CustomTransitiveClosureLink.find(:first, :conditions => { :the_ancestor_id => n1.id, :the_descendant_id => n3.id })
135
+ assert_nil tc, 'expected to not find path from n1 to n3'
136
+ end
137
+ end
138
+ end
139
+ end