acts_as_oqgraph 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -42,6 +42,7 @@ Then I can call:
42
42
  and I get the whole tree of friends of friends etc...
43
43
 
44
44
  Imagine you have a maze to solve. With OQGraph the solution is as simple as: start_cell.shortest_path_to(finish_cell).
45
+ See the demo code at http://github.com/stuart/acts_as_oqgraph_demo
45
46
 
46
47
  It's good for representing tree structures, networks, routes between cities etc.
47
48
 
@@ -149,8 +150,9 @@ The OQGraph table will also get created if it does not exist. The OQGraph table
149
150
  memory only. The table structure is not volatile and gets stored in the db.
150
151
  When your application starts up it will put all the edges into the graph table and update them as
151
152
  they are created, deleted or modified. This could slow things down at startup but caching classes in production
152
- means it does not happen on every request. I will add functionality to only update the oqgraph table if the
153
- db server has been stopped, once I figure out the best way to do it.
153
+ means it does not happen on every request. The graph table is only rewritten now when the DB has been restarted.
154
+ You can use this code to force the graph to be rebuilt:
155
+ NodeModel.rebuild_graph
154
156
 
155
157
  == How fast is it?
156
158
  I've tested with an application with 10000 nodes and 0 to 9 links from each.
@@ -175,15 +177,14 @@ Of course YMMV.
175
177
  == Hairy bits, bugs and gotchas
176
178
 
177
179
  To keep the oqgraph table up to date the edge model copies all of it records in when first instantiated.
178
- This means that if you do not have
179
- config.cache_classes = true
180
- in your environment and you have a lot of nodes your app will be dog slow. Also the app will be slow to start at first if
181
- there are many nodes even when caches are cached.
180
+ This means that on first startup the app can be slow to respond until the whole graph has been written.
181
+ This should not need to happen again unless the DB is restarted. You can get the MySQL server to update the graph
182
+ by using the --init-file=<SQLfile> option in my.cnf with the appropriate SQL in it.
182
183
 
183
184
  I've encountered a bug where the oqgraph table occasionally needs to be dropped and rebuilt. It's being tracked down.
184
185
  If you are not getting any results from the oqgrah table try dropping it and restarting the app.
185
186
 
186
- I'm working on a way to tell if the oqgraph table is stale. Suggestions would be welcome.
187
+ I'm working on a way to tell if the oqgraph table is stale other than by the current count of rows. Suggestions would be welcome.
187
188
 
188
189
  There is a method 'in_edges' in the edge model class. This does not currently work. 'out_edges' does.
189
190
 
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ begin
6
6
  Jeweler::Tasks.new do |gem|
7
7
  gem.name = "acts_as_oqgraph"
8
8
  gem.summary = %Q{Use the Open Query Graph engine with Active Record}
9
- gem.description = %Q{Acts As OQGraph allows ActiveRecord models to use the fast ans powerful OQGraph engine for MYSQL.}
9
+ gem.description = %Q{Acts As OQGraph allows ActiveRecord models to use the fast and powerful OQGraph engine for MYSQL.}
10
10
  gem.email = "stuart.coyle@gmail.com"
11
11
  gem.homepage = "http://github.com/stuart/acts_as_oqgraph"
12
12
  gem.authors = ["Stuart Coyle"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.6
1
+ 0.1.7
@@ -5,12 +5,12 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{acts_as_oqgraph}
8
- s.version = "0.1.6"
8
+ s.version = "0.1.7"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Stuart Coyle"]
12
- s.date = %q{2010-07-22}
13
- s.description = %q{Acts As OQGraph allows ActiveRecord models to use the fast ans powerful OQGraph engine for MYSQL.}
12
+ s.date = %q{2011-06-06}
13
+ s.description = %q{Acts As OQGraph allows ActiveRecord models to use the fast and powerful OQGraph engine for MYSQL.}
14
14
  s.email = %q{stuart.coyle@gmail.com}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
  s.homepage = %q{http://github.com/stuart/acts_as_oqgraph}
35
35
  s.rdoc_options = ["--charset=UTF-8"]
36
36
  s.require_paths = ["lib"]
37
- s.rubygems_version = %q{1.3.6}
37
+ s.rubygems_version = %q{1.3.7}
38
38
  s.summary = %q{Use the Open Query Graph engine with Active Record}
39
39
  s.test_files = [
40
40
  "test/helper.rb",
@@ -47,7 +47,7 @@ Gem::Specification.new do |s|
47
47
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
48
  s.specification_version = 3
49
49
 
50
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
51
  else
52
52
  end
53
53
  else
@@ -36,7 +36,9 @@ module OQGraph
36
36
  # The field names and table name can be changed via the options listed above.
37
37
  #
38
38
  # The gem will automatically create the oqgraph table.
39
- #
39
+ # To rebuild the oqgraph table do:
40
+ # Model.rebuild_graph
41
+ #
40
42
  # Examples of use:
41
43
  #
42
44
  # Creating and removing edges:
@@ -127,9 +129,14 @@ module OQGraph
127
129
  '#{edge_table_name}'
128
130
  end
129
131
 
132
+ def self.rebuild_graph
133
+ edge_class.create_graph_table
134
+ end
130
135
  EOF
131
136
  end
132
137
 
138
+
139
+
133
140
  private
134
141
 
135
142
  # Check that we have the OQGraph engine plugin installed in MySQL
data/lib/graph_edge.rb CHANGED
@@ -6,13 +6,8 @@ class GraphEdge < ActiveRecord::Base
6
6
  after_destroy :remove_from_graph
7
7
  after_update :update_graph
8
8
 
9
- # Creates the OQgraph table if it does not exist.
10
- # Deletes all entries if it does exist and then repopulates with
11
- # current edges. TODO: Optimise this so that it only does so if the
12
- # DB server has been restarted rather than when the app is restarted.
13
- def self.create_graph_table
14
- connection.execute "DROP TABLE IF EXISTS #{oqgraph_table_name}"
15
-
9
+ # Creates the OQgraph table if it does not exist and populate with edges.
10
+ def self.create_graph_table
16
11
  connection.execute <<-EOS
17
12
  CREATE TABLE IF NOT EXISTS #{oqgraph_table_name} (
18
13
  latch SMALLINT UNSIGNED NULL,
@@ -24,12 +19,15 @@ class GraphEdge < ActiveRecord::Base
24
19
  KEY (latch, origid, destid) USING HASH,
25
20
  KEY (latch, destid, origid) USING HASH
26
21
  ) ENGINE=OQGRAPH;
27
- EOS
22
+ EOS
28
23
 
29
- connection.execute <<-EOS
30
- INSERT INTO #{oqgraph_table_name} (origid, destid, weight)
31
- SELECT #{from_key}, #{to_key}, #{weight_column} FROM #{table_name}
24
+ # if the DB server has restarted then there will be no records in the oqgraph table.
25
+ if !up_to_date?
26
+ connection.execute <<-EOS
27
+ REPLACE INTO #{oqgraph_table_name} (origid, destid, weight)
28
+ SELECT #{from_key}, #{to_key}, #{weight_column} FROM #{table_name}
32
29
  EOS
30
+ end
33
31
  end
34
32
 
35
33
  # Returns the shortest path from node to node.
@@ -75,7 +73,7 @@ class GraphEdge < ActiveRecord::Base
75
73
 
76
74
  # Finds the edges leading directly into the node
77
75
  # FIXME: Note this currently does not work.
78
- # I suspect a bug in OQGRaph engine.
76
+ # I suspect a bug in OQGraph engine.
79
77
  # Using the node classes incoming_nodes is equivalent to this.
80
78
  def self.in_edges(node)
81
79
  sql = <<-EOS
@@ -135,4 +133,8 @@ private
135
133
  def self.node_table
136
134
  node_class.table_name
137
135
  end
136
+
137
+ def self.up_to_date?
138
+ connection.select_value("SELECT COUNT(*) FROM #{oqgraph_table_name}") == connection.select_value("SELECT COUNT(*) FROM #{table_name}")
139
+ end
138
140
  end
@@ -15,8 +15,8 @@ class TestActsAsOqgraph < ActiveSupport::TestCase
15
15
  ActiveRecord::Base.connection.execute("CREATE TABLE IF NOT EXISTS custom_edges(id INTEGER DEFAULT NULL AUTO_INCREMENT PRIMARY KEY, orig_id INTEGER, dest_id INTEGER, length DOUBLE);")
16
16
 
17
17
  # These requires need the tables to be created.
18
- require File.join(File.dirname(__FILE__),'models/custom_test_model')
19
- require File.join(File.dirname(__FILE__),'models/test_model')
18
+ require File.join(File.expand_path(File.dirname(__FILE__)),"models/custom_test_model")
19
+ require File.join(File.expand_path(File.dirname(__FILE__)),"models/test_model")
20
20
 
21
21
  @test_1 = TestModel.create(:name => 'a')
22
22
  @test_2 = TestModel.create(:name => 'b')
@@ -134,7 +134,7 @@ class TestActsAsOqgraph < ActiveSupport::TestCase
134
134
  end
135
135
 
136
136
  def test_getting_shortest_path_more_complex
137
- #
137
+ #
138
138
  # a -- b -- c -- d
139
139
  # | /
140
140
  # e-- f
@@ -222,12 +222,27 @@ class TestActsAsOqgraph < ActiveSupport::TestCase
222
222
  end
223
223
  end
224
224
 
225
+ # There's an odd case here where MySQL would raise an error only when using Rails.
225
226
  def test_deletion_of_nonexistent_edge_raises_error
226
227
  edge = @test_1.create_edge_to @test_2
227
228
  ActiveRecord::Base.connection.execute("DELETE FROM test_model_oqgraph WHERE destid = #{edge.to_id} AND origid = #{edge.from_id}")
228
229
  assert_nothing_raised do
229
230
  edge.destroy
230
231
  end
231
- end
232
+ end
233
+
234
+ def test_rebuild_graph
235
+ @test_1.create_edge_to @test_2
236
+ @test_2.create_edge_to @test_3
237
+ @test_3.create_edge_to @test_4
238
+ # Simulate the DB restart
239
+ ActiveRecord::Base.connection.execute("DELETE FROM test_model_oqgraph;")
240
+ TestModel.rebuild_graph
232
241
 
242
+ assert_equal [@test_1, @test_2, @test_3, @test_4], @test_1.shortest_path_to(@test_4)
243
+ assert_equal ['a','b','c','d'], @test_1.shortest_path_to(@test_4).map(&:name)
244
+
245
+ end
246
+
247
+
233
248
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 6
9
- version: 0.1.6
8
+ - 7
9
+ version: 0.1.7
10
10
  platform: ruby
11
11
  authors:
12
12
  - Stuart Coyle
@@ -14,11 +14,11 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-07-22 00:00:00 +10:00
17
+ date: 2011-06-06 00:00:00 +10:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
21
- description: Acts As OQGraph allows ActiveRecord models to use the fast ans powerful OQGraph engine for MYSQL.
21
+ description: Acts As OQGraph allows ActiveRecord models to use the fast and powerful OQGraph engine for MYSQL.
22
22
  email: stuart.coyle@gmail.com
23
23
  executables: []
24
24
 
@@ -51,6 +51,7 @@ rdoc_options:
51
51
  require_paths:
52
52
  - lib
53
53
  required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
54
55
  requirements:
55
56
  - - ">="
56
57
  - !ruby/object:Gem::Version
@@ -58,6 +59,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
58
59
  - 0
59
60
  version: "0"
60
61
  required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
61
63
  requirements:
62
64
  - - ">="
63
65
  - !ruby/object:Gem::Version
@@ -67,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
69
  requirements: []
68
70
 
69
71
  rubyforge_project:
70
- rubygems_version: 1.3.6
72
+ rubygems_version: 1.3.7
71
73
  signing_key:
72
74
  specification_version: 3
73
75
  summary: Use the Open Query Graph engine with Active Record