dagnabit 2.2.1

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.
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