dagnabit 2.2.1 → 2.2.2
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 +10 -12
- data/VERSION.yml +1 -1
- data/bin/dagnabit-test +1 -1
- data/dagnabit.gemspec +5 -3
- data/lib/dagnabit/link/transitive_closure_recalculation/on_update.rb +4 -0
- data/test/benchmark/helper.rb +43 -0
- data/test/benchmark/linear_benchmark.rb +41 -0
- data/test/dagnabit/link/test_transitive_closure_recalculation.rb +11 -0
- metadata +4 -2
data/README.rdoc
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
= This is the GitHub branch
|
2
|
-
|
3
|
-
This branch is updated irregularly. The canonical dagnabit repository is hosted on Gitorious:
|
4
|
-
|
5
|
-
http://gitorious.org/dagnabit/dagnabit
|
6
|
-
|
7
1
|
= dagnabit
|
8
2
|
|
9
3
|
dagnabit is (yet another) ActiveRecord plugin for directed acyclic graphs. It
|
@@ -83,6 +77,9 @@ In your node models, put
|
|
83
77
|
+acts_as_dag_link+ accepts some configuration options. These configuration
|
84
78
|
options are discussed below.
|
85
79
|
|
80
|
+
For a code example of a complete setup, see the <tt>dagnabit-test</tt> script
|
81
|
+
in +bin+.
|
82
|
+
|
86
83
|
=== Link table schemas
|
87
84
|
|
88
85
|
Link storage is accomplished with two tables having identical schemas. One
|
@@ -109,6 +106,9 @@ The behavior of the plugin with a deviant schema is unspecified.
|
|
109
106
|
* +transitive_closure_table_name+: The table where tuples in the transitive
|
110
107
|
closure of the graph should be stored. Defaults to
|
111
108
|
<tt>#{link_table_name}_transitive_closure_tuples</tt>.
|
109
|
+
* +transitive_closure_class_name+: The transitive closure is represented with
|
110
|
+
an ActiveRecord class whose name defaults to +TransitiveClosureLink+. Set this
|
111
|
+
option to change that name.
|
112
112
|
* +descendant_id_column+: Column in the link tables for recording the ID of a
|
113
113
|
descendant. Defaults to +descendant_id+.
|
114
114
|
* +ancestor_id_column+: Column in the link tables for recording the ID of a
|
@@ -146,22 +146,20 @@ edge model was called +Link+, these would be valid expressions:
|
|
146
146
|
|
147
147
|
+acts_as_dag_node+ adds the following instance methods to nodes:
|
148
148
|
|
149
|
-
* +
|
149
|
+
* +children+: All children of the node. Read-only.
|
150
|
+
* +parents+: All parents of the node. Read-only.
|
150
151
|
* +ancestors+: All ancestors of the node. Read-only.
|
152
|
+
* +descendants+: All descendants of the node. Read-only.
|
151
153
|
|
152
154
|
=== Link interface
|
153
155
|
|
154
156
|
The following class methods are available on links:
|
155
157
|
|
156
158
|
* +paths+: Retrieves all paths from one node to another.
|
157
|
-
*
|
158
|
-
* +find_edge+: Finds an edge from one node to another, or nil if none exists.
|
159
|
+
* <tt>path?</tt>: Returns whether a node is reachable from another node.
|
159
160
|
* +build_edge+: Builds an edge from one node to another node.
|
160
161
|
* +connect+: Builds and saves an edge from one node to another. Identical to
|
161
162
|
<tt>build_edge(nodeA, nodeB).save</tt>.
|
162
|
-
* <tt>connect!</tt>: Builds and saves an edge from one node to another, raising
|
163
|
-
an exception if save fails. Identical to <tt>build_edge(nodeA,
|
164
|
-
nodeB).save!</tt>.
|
165
163
|
|
166
164
|
== Introduction to the codebase
|
167
165
|
|
data/VERSION.yml
CHANGED
data/bin/dagnabit-test
CHANGED
data/dagnabit.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dagnabit}
|
8
|
-
s.version = "2.2.
|
8
|
+
s.version = "2.2.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["David Yip"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-02-04}
|
13
13
|
s.default_executable = %q{dagnabit-test}
|
14
14
|
s.email = %q{yipdw@northwestern.edu}
|
15
15
|
s.executables = ["dagnabit-test"]
|
@@ -75,7 +75,9 @@ Gem::Specification.new do |s|
|
|
75
75
|
s.rubygems_version = %q{1.3.5}
|
76
76
|
s.summary = %q{Directed acyclic graph plugin for ActiveRecord}
|
77
77
|
s.test_files = [
|
78
|
-
"test/
|
78
|
+
"test/benchmark/helper.rb",
|
79
|
+
"test/benchmark/linear_benchmark.rb",
|
80
|
+
"test/connections/native_postgresql/connection.rb",
|
79
81
|
"test/connections/native_sqlite3/connection.rb",
|
80
82
|
"test/dagnabit/link/test_associations.rb",
|
81
83
|
"test/dagnabit/link/test_class_methods.rb",
|
@@ -3,7 +3,11 @@ module Dagnabit
|
|
3
3
|
module TransitiveClosureRecalculation
|
4
4
|
module OnUpdate
|
5
5
|
def after_update
|
6
|
+
current_values = dag_link_column_names.map { |n| connection.quote(send(n)) }
|
6
7
|
old_values = dag_link_column_names.map { |n| connection.quote(changes[n].try(:first) || send(n)) }
|
8
|
+
|
9
|
+
return unless current_values != old_values
|
10
|
+
|
7
11
|
update_transitive_closure_for_destroy(*old_values)
|
8
12
|
update_transitive_closure_for_create
|
9
13
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_support/test_case'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'dagnabit'
|
9
|
+
|
10
|
+
ActiveRecord::Base.logger = Logger.new("benchmark.log")
|
11
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define do
|
14
|
+
[:links, :links_transitive_closure_tuples].each do |table|
|
15
|
+
create_table table do |t|
|
16
|
+
t.integer :ancestor_id
|
17
|
+
t.integer :descendant_id
|
18
|
+
t.string :ancestor_type
|
19
|
+
t.string :descendant_type
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
create_table :nodes do |t|
|
24
|
+
t.string :data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Link < ActiveRecord::Base
|
29
|
+
acts_as_dag_link
|
30
|
+
end
|
31
|
+
|
32
|
+
class Node < ActiveRecord::Base
|
33
|
+
acts_as_dag_node_linked_by 'Link'
|
34
|
+
end
|
35
|
+
|
36
|
+
REPETITIONS = 5
|
37
|
+
|
38
|
+
class BenchmarkTestCase < ActiveSupport::TestCase
|
39
|
+
def teardown
|
40
|
+
Node.delete_all
|
41
|
+
Link.delete_all
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
2
|
+
|
3
|
+
class LinearBenchmark < BenchmarkTestCase
|
4
|
+
def test_speed
|
5
|
+
Benchmark.bm do |x|
|
6
|
+
root = Node.create
|
7
|
+
last_node = root
|
8
|
+
|
9
|
+
100.times do |i|
|
10
|
+
target = Node.create
|
11
|
+
|
12
|
+
x.report("Graph depth: #{i+1}") { Link.connect(last_node, target) }
|
13
|
+
|
14
|
+
assert_equal 1, last_node.children.length
|
15
|
+
assert_equal i+1, root.descendants.length
|
16
|
+
|
17
|
+
last_node = target
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_deletion_from_long_list
|
23
|
+
nodes = []
|
24
|
+
last_node = Node.create
|
25
|
+
|
26
|
+
puts 'Building long list...'
|
27
|
+
50.times do |i|
|
28
|
+
target = Node.create
|
29
|
+
Link.connect(last_node, target)
|
30
|
+
last_node = target
|
31
|
+
|
32
|
+
nodes << target
|
33
|
+
end
|
34
|
+
|
35
|
+
Benchmark.bm do |x|
|
36
|
+
nodes.each_with_index do |node, i|
|
37
|
+
x.report("Destroying node #{i}") { node.destroy }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -134,6 +134,17 @@ module Dagnabit
|
|
134
134
|
tc = CustomTransitiveClosureLink.find(:first, :conditions => { :the_ancestor_id => n1.id, :the_descendant_id => n3.id })
|
135
135
|
assert_nil tc, 'expected to not find path from n1 to n3'
|
136
136
|
end
|
137
|
+
|
138
|
+
should "not recalculate transitive closure if neither a link's source nor target changed" do
|
139
|
+
n1 = ::Node.create
|
140
|
+
n2 = ::Node.create
|
141
|
+
|
142
|
+
l1 = ::Link.new(:ancestor => n1, :descendant => n2)
|
143
|
+
|
144
|
+
l1.expects(:update_transitive_closure_for_destroy).never
|
145
|
+
l1.save
|
146
|
+
l1.save
|
147
|
+
end
|
137
148
|
end
|
138
149
|
end
|
139
150
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dagnabit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Yip
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-02-04 00:00:00 -06:00
|
13
13
|
default_executable: dagnabit-test
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -141,6 +141,8 @@ signing_key:
|
|
141
141
|
specification_version: 3
|
142
142
|
summary: Directed acyclic graph plugin for ActiveRecord
|
143
143
|
test_files:
|
144
|
+
- test/benchmark/helper.rb
|
145
|
+
- test/benchmark/linear_benchmark.rb
|
144
146
|
- test/connections/native_postgresql/connection.rb
|
145
147
|
- test/connections/native_sqlite3/connection.rb
|
146
148
|
- test/dagnabit/link/test_associations.rb
|