graphshaper 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,3 +5,8 @@ rvm:
5
5
  - jruby-19mode
6
6
  - rbx-19mode
7
7
  - ruby-head
8
+ - jruby-head
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: ruby-head
12
+ - rvm: jruby-head
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Graphshaper
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/moonglum/graphshaper.png?branch=master)](http://travis-ci.org/moonglum/graphshaper)
4
+ [![Dependency Status](https://gemnasium.com/moonglum/graphshaper.png)](https://gemnasium.com/moonglum/graphshaper)
5
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/moonglum/graphshaper)
4
6
 
5
7
  Graphshaper can generate realistic, scale-free graphs of any size. It is tested with MRI Ruby (1.9.2. and 1.9.3) and the 1.9 versions of jRuby and Rubinius.
6
8
 
@@ -26,7 +28,7 @@ The commandline tool expects one argument: The number of vertices you want your
26
28
 
27
29
  You can use the following options:
28
30
 
29
- * `-a`, `--avocado`: Store the graph in a local AvocadoDB instance
31
+ * `-a`, `--arango`: Store the graph in a local ArangoDB instance
30
32
  * `-l`, `--log`: Store the graph in two CSV files for nodes and edges
31
33
  * `-d`, `--dot`: Store the graph in the dot format
32
34
  * `-p`, `--png`: Export the graph as a PNG (you need to install graphviz for that – circo is used for the layout)
@@ -9,37 +9,41 @@ options = {}
9
9
 
10
10
  optparse = OptionParser.new do |opts|
11
11
  opts.banner = "Usage: graphshaper [options] size"
12
-
13
- opts.on("-a", "--avocado", "Store the graph in a local AvocadoDB instance") do |avocado|
14
- options[:avocado] = avocado
12
+
13
+ opts.on("-a", "--arango", "Store the graph in a local ArangoDB instance") do |arango|
14
+ options[:arango] = arango
15
15
  end
16
-
16
+
17
17
  opts.on("-l", "--log", "Store the graph in two CSV files for nodes and edges") do |log|
18
18
  options[:log] = log
19
19
  end
20
-
20
+
21
21
  opts.on("-d", "--dot", "Store the graph in the dot format") do |dot|
22
22
  options[:dot] = dot
23
23
  end
24
-
24
+
25
25
  opts.on("-s", "--sql", "Store the graph in sql format") do |sql|
26
26
  options[:sql] = sql
27
27
  end
28
-
28
+
29
29
  opts.on("-p", "--png", "Export the graph as a PNG (graphviz must be installed)") do |png|
30
30
  options[:dot] = png
31
31
  options[:png] = png
32
32
  end
33
-
33
+
34
34
  opts.on("-t", "--testdata", "Generate Test Data (for shortestpath)") do |sp|
35
35
  options[:t] = sp
36
36
  end
37
-
37
+
38
+ opts.on("-j", "--json", "Generate JSON files") do |j|
39
+ options[:json] = j
40
+ end
41
+
38
42
  opts.on_tail("--version", "Show version") do
39
43
  puts Graphshaper::VERSION
40
44
  exit
41
45
  end
42
-
46
+
43
47
  opts.parse!
44
48
  end
45
49
 
@@ -54,26 +58,26 @@ if options[:png] and `which circo` == ""
54
58
  exit
55
59
  end
56
60
 
57
- # check for running component: AvocadoDB
58
- if options[:avocado]
61
+ # check for running component: ArangoDB
62
+ if options[:arango]
59
63
  begin
60
64
  HTTParty.get 'http://localhost:8529'
61
65
  rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
62
- puts "No AvocadoDB instance is running on port 8529. Please start AvocadoDB or run without --avocado option."
66
+ puts "No ArangoDB instance is running on port 8529. Please start ArangoDB or run without --arango option."
63
67
  exit
64
68
  end
65
69
  end
66
70
 
67
71
  adapters = []
68
-
72
+
69
73
  if options[:log]
70
74
  generated_vertices_csv_file = File.new("generated_vertices.csv", "w")
71
75
  generated_edges_csv_file = File.new("generated_edges.csv", "w")
72
76
  adapters << Graphshaper::LoggingAdapter.new(generated_vertices_csv_file, generated_edges_csv_file)
73
77
  end
74
78
 
75
- if options[:avocado]
76
- adapters << Graphshaper::AvocadoDbAdapter.new("vertices", "edges")
79
+ if options[:arango]
80
+ adapters << Graphshaper::ArangoDbAdapter.new("vertices", "edges")
77
81
  end
78
82
 
79
83
  if options[:dot]
@@ -85,10 +89,16 @@ if options[:sql]
85
89
  generated_graph_sql_file = File.new("generated_graph.sql", "w")
86
90
  generated_vertices_sql_file = File.new("generated_vertices.sql", "w")
87
91
  generated_edges_sql_file = File.new("generated_edges.sql", "w")
88
-
92
+
89
93
  adapters << Graphshaper::SqlAdapter.new(generated_graph_sql_file, generated_vertices_sql_file, generated_edges_sql_file)
90
94
  end
91
95
 
96
+ if options[:json]
97
+ `rm -r generated_edges`
98
+ Dir.mkdir("generated_edges")
99
+ adapters << Graphshaper::JsonAdapter.new("generated_edges")
100
+ end
101
+
92
102
  number_of_vertices = ARGV.first.to_i
93
103
  inner_vertices = 20
94
104
  outer_vertices = number_of_vertices - inner_vertices
@@ -116,11 +126,12 @@ end
116
126
 
117
127
  print "#{graph.order} vertices and #{graph.size} edges generated"
118
128
 
119
- print " and saved into AvocadoDB" if options[:avocado]
129
+ print " and saved into ArangoDB" if options[:arango]
120
130
  print " and logged" if options[:log]
121
131
  print " and generated as a dot" if options[:dot]
122
132
  print " and saved in sql format" if options[:sql]
123
133
  print " and generated test cases" if options[:t]
134
+ print " and generated JSON files" if options[:json]
124
135
 
125
136
  if options[:png]
126
137
  system('circo -Tpng generated_graph.dot -o generated_graph.png')
@@ -134,4 +145,4 @@ elsif ellapsed_time < 60
134
145
  puts " in about #{ellapsed_time.round} seconds"
135
146
  else
136
147
  puts " in about #{ellapsed_time.round / 60} minutes and #{ellapsed_time.round % 60} seconds"
137
- end
148
+ end
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
5
5
  gem.authors = ["Lucas Dohmen"]
6
6
  gem.email = ["me@moonglum.net"]
7
7
  gem.description = %q{Generate realistic graphs}
8
- gem.summary = %q{Graphshaper can generate realistic, scale-free graphs of any size.}
8
+ gem.summary = %q{Graphshaper can generate realistic, scale-free graphs of any size for different databases.}
9
9
  gem.homepage = "http://moonglum.github.com/graphshaper"
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
@@ -14,10 +14,10 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "graphshaper"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Graphshaper::VERSION
17
-
18
- gem.add_dependency "httparty", "~> 0.8.1"
19
- gem.add_development_dependency "rake", "~> 0.9.2.2"
20
- gem.add_development_dependency "rspec", "~> 2.9.0"
21
- gem.add_development_dependency "yard", "~> 0.7.5"
22
- gem.add_development_dependency "webmock", "~> 1.8.6"
17
+
18
+ gem.add_dependency "httparty", "~> 0.10.2"
19
+ gem.add_development_dependency "rake", "~> 10.0.3"
20
+ gem.add_development_dependency "rspec", "~> 2.13.0"
21
+ gem.add_development_dependency "webmock", "~> 1.11.0"
22
+ gem.add_development_dependency "yard", "~> 0.8.5.2"
23
23
  end
@@ -1,9 +1,10 @@
1
1
  require "graphshaper/version"
2
2
  require "graphshaper/undirected_graph"
3
- require "graphshaper/adapters/avocadodb_adapter"
3
+ require "graphshaper/adapters/arangodb_adapter"
4
4
  require "graphshaper/adapters/logging_adapter"
5
5
  require "graphshaper/adapters/dot_adapter"
6
6
  require "graphshaper/adapters/sql_adapter"
7
+ require "graphshaper/adapters/json_adapter"
7
8
 
8
9
  module Graphshaper
9
10
  end
@@ -1,47 +1,47 @@
1
1
  require "httparty"
2
2
 
3
3
  module Graphshaper
4
- class AvocadoDbAdapter
4
+ class ArangoDbAdapter
5
5
  include HTTParty
6
-
6
+
7
7
  base_uri 'localhost:8529'
8
8
  format :json
9
-
9
+
10
10
  def initialize(vertex_collection_name, edge_collection_name)
11
11
  @vertex_collection_name = vertex_collection_name
12
12
  @edge_collection_name = edge_collection_name
13
13
  @vertex_matching = []
14
14
  @edge_matching = []
15
-
15
+
16
16
  [@vertex_collection_name, @edge_collection_name].each { |collection| drop_and_create_collection collection}
17
17
  end
18
-
18
+
19
19
  def add_vertex(id)
20
20
  cmd = "/document?collection=#{@vertex_collection_name}"
21
- body = "{ \"id\" : \"#{id}\" }"
22
- response = self.class.post(cmd, :body => body)
21
+ body = "{ \"id\" : \"#{id}\" }"
22
+ response = self.class.post(cmd, :body => body)
23
23
  @vertex_matching[id] = response.parsed_response["_id"]
24
24
  end
25
-
25
+
26
26
  def add_edge(edge_id, from, to)
27
27
  database_id_for_first_node = @vertex_matching[from]
28
28
  database_id_for_second_node = @vertex_matching[to]
29
-
29
+
30
30
  cmd = "/edge?collection=#{@edge_collection_name}&from=#{database_id_for_first_node}&to=#{database_id_for_second_node}"
31
- body = "{ \"id\" : \"#{edge_id}\" }"
32
- response = self.class.post(cmd, :body => body)
31
+ body = "{ \"id\" : \"#{edge_id}\" }"
32
+ response = self.class.post(cmd, :body => body)
33
33
  @edge_matching[edge_id] = response.parsed_response["_id"]
34
34
  end
35
-
35
+
36
36
  def close
37
-
37
+
38
38
  end
39
-
39
+
40
40
  private
41
-
41
+
42
42
  def drop_and_create_collection(name)
43
43
  self.class.delete "/_api/collection/#{name}"
44
44
  self.class.post "/_api/collection", body: "{ \"name\" : \"#{name}\"}"
45
45
  end
46
46
  end
47
- end
47
+ end
@@ -4,18 +4,18 @@ module Graphshaper
4
4
  @output_file = output_file
5
5
  @output_file << "digraph genereated_graph { \n rankdir=LR;\n node [shape = circle];\n edge [dir=none];\n"
6
6
  end
7
-
7
+
8
8
  def add_edge(edge_id, in_id, out_id)
9
9
  @output_file << " #{in_id} -> #{out_id} [ label = \"#{edge_id}\" ];\n"
10
10
  end
11
-
11
+
12
12
  def add_vertex(vertex_id)
13
13
  @output_file << " #{vertex_id};\n"
14
14
  end
15
-
15
+
16
16
  def close
17
17
  @output_file << "}"
18
18
  @output_file.close
19
19
  end
20
20
  end
21
- end
21
+ end
@@ -0,0 +1,40 @@
1
+ module Graphshaper
2
+ # An adapter to generate JSON files in the format specified by Michael Hackstein
3
+ class JsonAdapter
4
+ def initialize(base_path, file_class = File)
5
+ @base_path = base_path
6
+ @file_class = file_class
7
+ @vertex_file_names = []
8
+ end
9
+
10
+ def add_edge(edge_id, from_id, to_id)
11
+ file_name = "#{@base_path}/#{from_id}.json"
12
+ file = @file_class.new(file_name, "a")
13
+ file.write(%Q<{"id":#{to_id}},>)
14
+ file.close
15
+ end
16
+
17
+ def add_vertex(vertex_id)
18
+ file_name = "#{@base_path}/#{vertex_id}.json"
19
+ @vertex_file_names << file_name
20
+ file = @file_class.new(file_name, "w")
21
+ file.write(file_header(vertex_id))
22
+ file.close
23
+ end
24
+
25
+ def close
26
+ @vertex_file_names.each do |file_name|
27
+ content = @file_class.read(file_name).gsub(/,\z/, '')
28
+ file = @file_class.new(file_name, "w+")
29
+ file.write("#{content}]}")
30
+ file.close
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def file_header(id)
37
+ %Q<{"id":#{id},"children" : [>
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,4 @@
1
1
  module Graphshaper
2
-
3
2
  # A simple adapter for logging the created graph
4
3
  class LoggingAdapter
5
4
  def initialize(vertex_logger_file, edge_logger_file)
@@ -8,18 +7,18 @@ module Graphshaper
8
7
  @vertex_logger_file << "vertex_id\n"
9
8
  @edge_logger_file << "edge_id,from_id,to_id\n"
10
9
  end
11
-
10
+
12
11
  def add_edge(edge_id, from_id, to_id)
13
12
  @edge_logger_file << "#{edge_id},#{from_id},#{to_id}\n"
14
13
  end
15
-
14
+
16
15
  def add_vertex(vertex_id)
17
16
  @vertex_logger_file << "#{vertex_id}\n"
18
17
  end
19
-
18
+
20
19
  def close
21
20
  @vertex_logger_file.close
22
21
  @edge_logger_file.close
23
22
  end
24
23
  end
25
- end
24
+ end
@@ -4,38 +4,38 @@ module Graphshaper
4
4
  @schema_file = schema_file
5
5
  @vertex_file = vertex_file
6
6
  @edge_file = edge_file
7
-
7
+
8
8
  File.readlines("templates/schema.sql").each do |schema_line|
9
9
  @schema_file << schema_line
10
10
  end
11
-
11
+
12
12
  @vertex_file << "LOCK TABLES `vertices` WRITE;\n"
13
13
  @vertex_file << "INSERT INTO `vertices` (`id`, `vertex_id`)\n"
14
14
  @vertex_file << "VALUES\n\t"
15
-
15
+
16
16
  @edge_file << "LOCK TABLES `edges` WRITE;\n"
17
17
  @edge_file << "INSERT INTO `edges` (`id`, `edge_id`, `from_id`, `to_id`)\n"
18
18
  @edge_file << "VALUES\n\t"
19
-
19
+
20
20
  @first_vertex = true
21
21
  @first_edge = true
22
22
  end
23
-
23
+
24
24
  def add_vertex(vertex_id)
25
25
  @vertex_file << ",\n\t" unless @first_vertex
26
26
  @vertex_file << "(#{vertex_id + 1},#{vertex_id})"
27
27
  @first_vertex = false
28
28
  end
29
-
29
+
30
30
  def add_edge(edge_id, from_id, to_id)
31
31
  @edge_file << ",\n\t" unless @first_edge
32
32
  @edge_file << "(#{edge_id + 1},#{edge_id},#{from_id + 1},#{to_id + 1})"
33
33
  @first_edge = false
34
34
  end
35
-
35
+
36
36
  def close
37
37
  [@vertex_file, @edge_file].each { |file| file << ";\nUNLOCK TABLES;"}
38
38
  [@schema_file, @vertex_file, @edge_file].each { |file| file.close }
39
39
  end
40
40
  end
41
- end
41
+ end
@@ -1,12 +1,12 @@
1
1
  require "set"
2
2
 
3
3
  module Graphshaper
4
-
4
+
5
5
  # A Graph is undirected when its edges do not have a direction.
6
6
  class UndirectedGraph
7
-
7
+
8
8
  # Create a graph with a given number of vertices and no edges.
9
- #
9
+ #
10
10
  # @param [Integer] number_of_vertices The number of vertices that the generated graph should have
11
11
  # @param [Hash] options_hash The options to create an undirected graph
12
12
  # @option options_hash [Array<Object>] :adapters An array of adapters you want to use
@@ -15,66 +15,66 @@ module Graphshaper
15
15
 
16
16
  @vertex_degrees = []
17
17
  @unconnected_vertices = Set.new
18
-
18
+
19
19
  number_of_vertices.times { add_vertex }
20
20
  @edges = Set.new
21
21
  end
22
-
22
+
23
23
  # The number of vertices.
24
24
  #
25
25
  # @return [Integer] Number of vertices.
26
26
  def order
27
27
  @vertex_degrees.length
28
28
  end
29
-
29
+
30
30
  # The number of edges.
31
- #
31
+ #
32
32
  # @return [Integer] Number of edges.
33
33
  def size
34
34
  @edges.length
35
35
  end
36
-
36
+
37
37
  # Connect all orphans with existing nodes
38
38
  def connect_all_vertices
39
39
  while number_of_orphans > 0
40
40
  vertex_orphan = orphans.shuffle.first
41
41
  while vertex_orphan == (random_vertex = rand(order)); end
42
-
42
+
43
43
  add_edge vertex_orphan, random_vertex
44
44
  end
45
45
  end
46
-
46
+
47
47
  # Add a new vertex to the graph.
48
48
  # If you call it with a block {|preferential_attachment| ... }, the block will be called for every existing vertex: An edge from the new vertex to this vertex will be created if and only if the block returns true.
49
49
  # @yield [preferential_attachment] The block that tests if the edge should be added.
50
50
  #
51
51
  # @yieldparam [Float] preferential_attachment The preferential attachment of the existing vertex
52
52
  # @yieldreturn [Boolean] Should the edge be added?
53
- #
53
+ #
54
54
  # @return [Integer] ID of the newly created vertex.
55
55
  def add_vertex(&block)
56
56
  new_vertex_id = @vertex_degrees.length
57
-
57
+
58
58
  @vertex_degrees << 0
59
59
  @unconnected_vertices << new_vertex_id
60
-
60
+
61
61
  unless @adapters.nil?
62
62
  @adapters.each do |adapter|
63
63
  adapter.add_vertex new_vertex_id
64
64
  end
65
65
  end
66
-
67
- if block_given?
66
+
67
+ if block_given?
68
68
  each_vertex_with_preferential_attachment do |vertex_id, preferential_attachment|
69
69
  add_edge new_vertex_id, vertex_id if block.call(preferential_attachment)
70
70
  end
71
71
  end
72
-
72
+
73
73
  new_vertex_id
74
74
  end
75
-
75
+
76
76
  # Add a new edge to the graph between two existing vertices.
77
- #
77
+ #
78
78
  # @param [Integer] first_vertex_id
79
79
  # @param [Integer] second_vertex_id
80
80
  # @raise [RuntimeError] The method throws a RuntimeError if you try to add a self-referential edge or an edge with a non-existing vertex.
@@ -89,7 +89,7 @@ module Graphshaper
89
89
  @unconnected_vertices.delete second_vertex_id
90
90
  @vertex_degrees[second_vertex_id] += 1
91
91
  @edges << [first_vertex_id, second_vertex_id].sort
92
-
92
+
93
93
  unless @adapters.nil?
94
94
  @adapters.each do |adapter|
95
95
  adapter.add_edge @edges.length - 1, first_vertex_id, second_vertex_id
@@ -97,7 +97,7 @@ module Graphshaper
97
97
  end
98
98
  end
99
99
  end
100
-
100
+
101
101
  # Tests, if an edge between the two vertices exists.
102
102
  #
103
103
  # @param [Integer] first_vertex_id
@@ -106,21 +106,21 @@ module Graphshaper
106
106
  def edge_between?(first_vertex_id, second_vertex_id)
107
107
  @edges.include? [first_vertex_id, second_vertex_id]
108
108
  end
109
-
109
+
110
110
  # The number of vertices without edges.
111
111
  #
112
112
  # @return [Integer] Number of vertices without edges.
113
113
  def number_of_orphans
114
114
  @unconnected_vertices.to_a.length
115
115
  end
116
-
116
+
117
117
  # The vertices without edges as an array.
118
118
  #
119
119
  # @return [Array<Integer>] IDs of the Vertices without edges.
120
120
  def orphans
121
121
  @unconnected_vertices.to_a
122
122
  end
123
-
123
+
124
124
  # Return the vertex degree for the vertex with the given ID.
125
125
  #
126
126
  # @param [Integer] vertex_id
@@ -128,7 +128,7 @@ module Graphshaper
128
128
  def vertex_degree_for(vertex_id)
129
129
  @vertex_degrees[vertex_id]
130
130
  end
131
-
131
+
132
132
  # Calculates the distribution of degrees in the graph. The value at the n-th position of the returned array is the number of vertices with n edges.
133
133
  #
134
134
  # @return [Array<Integer>] The degree distribution as an array of Integers.
@@ -143,16 +143,16 @@ module Graphshaper
143
143
  end
144
144
  degree_distribution
145
145
  end
146
-
146
+
147
147
  # Return the sum of all degrees.
148
148
  #
149
149
  # @return [Integer] Sum of all degrees
150
150
  def sum_of_all_degrees
151
151
  @edges.length * 2
152
152
  end
153
-
153
+
154
154
  # Iterate over all vertices of the graph. Call it with a block {|vertex_id, preferential_attachment| ... }.
155
- #
155
+ #
156
156
  # @yield [vertex_id, preferential_attachment] The block that tests if the edge should be added.
157
157
  # @yieldparam [Integer] vertex_id The preferential attachment of the existing vertex
158
158
  # @yieldparam [Float] preferential_attachment The preferential attachment of the existing vertex
@@ -167,4 +167,4 @@ module Graphshaper
167
167
  end
168
168
  end
169
169
  end
170
- end
170
+ end
@@ -1,3 +1,3 @@
1
1
  module Graphshaper
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,46 +1,46 @@
1
1
  require "spec_helper"
2
- require "graphshaper/adapters/avocadodb_adapter"
2
+ require "graphshaper/adapters/arangodb_adapter"
3
3
 
4
- describe Graphshaper::AvocadoDbAdapter do
4
+ describe Graphshaper::ArangoDbAdapter do
5
5
  before :each do
6
6
  stub_request(:delete, "http://localhost:8529/_api/collection/vertices").to_return(:status => 200, :body => "", :headers => {})
7
7
  stub_request(:post, "http://localhost:8529/_api/collection").with(:body => "{ \"name\" : \"vertices\"}")
8
8
  stub_request(:delete, "http://localhost:8529/_api/collection/edges").to_return(:status => 200, :body => "", :headers => {})
9
9
  stub_request(:post, "http://localhost:8529/_api/collection").with(:body => "{ \"name\" : \"edges\"}")
10
10
  stub_request(:post, "http://localhost:8529/document?collection=vertices")
11
- .with(:body => "{ \"id\" : \"1\" }")
12
- .to_return(:body => "{ \"_rev\": 26445614, \"_id\": \"73482/26445614\", \"error\": false }", :status => 200 )
11
+ .with(:body => "{ \"id\" : \"1\" }")
12
+ .to_return(:body => "{ \"_rev\": 26445614, \"_id\": \"73482/26445614\", \"error\": false }", :status => 200 )
13
13
  stub_request(:post, "http://localhost:8529/document?collection=vertices")
14
- .with(:body => "{ \"id\" : \"2\" }")
15
- .to_return(:body => "{ \"_rev\": 26445614, \"_id\": \"73482/26445615\", \"error\": false }", :status => 200 )
14
+ .with(:body => "{ \"id\" : \"2\" }")
15
+ .to_return(:body => "{ \"_rev\": 26445614, \"_id\": \"73482/26445615\", \"error\": false }", :status => 200 )
16
16
  stub_request(:post, "http://localhost:8529/edge?collection=edges&from=73482/26445614&to=73482/26445615")
17
- .with(:body => "{ \"id\" : \"7\" }")
18
- .to_return(:body => "{ \"_rev\": 9683012, \"_id\": \"7848004/9683012\", \"error\": false }", :status => 200)
19
-
20
- @avocado_adapter = Graphshaper::AvocadoDbAdapter.new "vertices", "edges"
17
+ .with(:body => "{ \"id\" : \"7\" }")
18
+ .to_return(:body => "{ \"_rev\": 9683012, \"_id\": \"7848004/9683012\", \"error\": false }", :status => 200)
19
+
20
+ @arango_adapter = Graphshaper::ArangoDbAdapter.new "vertices", "edges"
21
21
  end
22
-
22
+
23
23
  it "should drop the collections on startup" do
24
24
  WebMock.should have_requested(:delete, "http://localhost:8529/_api/collection/vertices")
25
25
  WebMock.should have_requested(:delete, "http://localhost:8529/_api/collection/edges")
26
26
  end
27
-
27
+
28
28
  it "should create the collections on startup" do
29
29
  WebMock.should have_requested(:post, "http://localhost:8529/_api/collection").with(:body => "{ \"name\" : \"vertices\"}")
30
30
  WebMock.should have_requested(:post, "http://localhost:8529/_api/collection").with(:body => "{ \"name\" : \"edges\"}")
31
31
  end
32
-
32
+
33
33
  it "should add a vertex" do
34
- @avocado_adapter.add_vertex 1
35
-
34
+ @arango_adapter.add_vertex 1
35
+
36
36
  WebMock.should have_requested(:post, "http://localhost:8529/document?collection=vertices").with(:body => "{ \"id\" : \"1\" }")
37
37
  end
38
-
38
+
39
39
  it "should add an edge with the correct vertex ids" do
40
- @avocado_adapter.add_vertex 1
41
- @avocado_adapter.add_vertex 2
42
- @avocado_adapter.add_edge 7, 1, 2
43
-
40
+ @arango_adapter.add_vertex 1
41
+ @arango_adapter.add_vertex 2
42
+ @arango_adapter.add_edge 7, 1, 2
43
+
44
44
  WebMock.should have_requested(:post, "http://localhost:8529/edge?collection=edges&from=73482/26445614&to=73482/26445615").with(:body => "{ \"id\" : \"7\" }")
45
45
  end
46
46
  end
@@ -6,31 +6,31 @@ describe Graphshaper::DotAdapter do
6
6
  @output_file = double()
7
7
  @output_file.stub :<<
8
8
  end
9
-
9
+
10
10
  it "should write a header into the file" do
11
11
  @output_file.should_receive(:<<).with("digraph genereated_graph { \n rankdir=LR;\n node [shape = circle];\n edge [dir=none];\n")
12
12
  Graphshaper::DotAdapter.new @output_file
13
13
  end
14
-
14
+
15
15
  describe "initialized Dot Adapter" do
16
16
  before :each do
17
17
  @dot_adapter = Graphshaper::DotAdapter.new @output_file
18
18
  end
19
-
19
+
20
20
  it "should be closeable" do
21
21
  @output_file.should_receive(:<<).with("}")
22
22
  @output_file.should_receive(:close)
23
23
  @dot_adapter.close
24
24
  end
25
-
25
+
26
26
  it "should write an edge in the correct format" do
27
27
  @output_file.should_receive(:<<).with(" 1 -> 2 [ label = \"0\" ];\n")
28
28
  @dot_adapter.add_edge(0,1,2)
29
29
  end
30
-
30
+
31
31
  it "should write a vertex in the correct format" do
32
32
  @output_file.should_receive(:<<).with(" 15;\n")
33
33
  @dot_adapter.add_vertex(15)
34
34
  end
35
35
  end
36
- end
36
+ end
@@ -0,0 +1,63 @@
1
+ require "spec_helper"
2
+ require "graphshaper/adapters/json_adapter"
3
+
4
+ describe Graphshaper::JsonAdapter do
5
+ describe "Initialized Adapter" do
6
+ let(:file_class) { double }
7
+ let(:vertex_id) { 1 }
8
+ let(:edge_id) { 2 }
9
+ let(:from_id) { 3 }
10
+ let(:to_id) { 4 }
11
+ subject { Graphshaper::JsonAdapter.new "test_dir", file_class }
12
+
13
+ it "should create a new edge file" do
14
+ file = double
15
+ file.should_receive(:write).with(%Q<{"id":#{vertex_id},"children" : [>)
16
+ file.should_receive(:close)
17
+ file_class.stub(:new).and_return { file }
18
+ file_class.should_receive(:new).with("test_dir/#{vertex_id}.json", "w")
19
+
20
+ subject.add_vertex(vertex_id)
21
+ end
22
+
23
+ it "should add edges to the vertices" do
24
+ file = double
25
+ file.should_receive(:write).with(%Q<{"id":#{to_id}},>)
26
+ file.should_receive(:close)
27
+ file_class.stub(:new).and_return { file }
28
+ file_class.should_receive(:new).with("test_dir/#{from_id}.json", "a")
29
+
30
+ subject.add_edge(edge_id, from_id, to_id)
31
+ end
32
+
33
+ it "should close all vertex files" do
34
+ file_1 = double
35
+ file_2 = double
36
+ close_file_1 = double
37
+ close_file_2 = double
38
+
39
+ [file_1, file_2, close_file_1, close_file_2].each do |f|
40
+ f.stub(:write)
41
+ f.stub(:close)
42
+ end
43
+
44
+ [close_file_1, close_file_2].each do |f|
45
+ f.should_receive(:write).with("a]}")
46
+ f.should_receive(:close)
47
+ end
48
+
49
+ file_class.stub(:read).with("test_dir/1.json").and_return { "a," }
50
+ file_class.stub(:read).with("test_dir/2.json").and_return { "a" }
51
+
52
+ file_class.stub(:new).with("test_dir/1.json", "w").and_return { file_1 }
53
+ file_class.stub(:new).with("test_dir/2.json", "w").and_return { file_2 }
54
+
55
+ file_class.stub(:new).with("test_dir/1.json", "w+").and_return { close_file_1 }
56
+ file_class.stub(:new).with("test_dir/2.json", "w+").and_return { close_file_2 }
57
+
58
+ subject.add_vertex(1)
59
+ subject.add_vertex(2)
60
+ subject.close
61
+ end
62
+ end
63
+ end
@@ -8,26 +8,26 @@ describe Graphshaper::LoggingAdapter do
8
8
  @edge_logger = double()
9
9
  @edge_logger.stub :<<
10
10
  end
11
-
11
+
12
12
  it "should write a header into the files" do
13
13
  @vertex_logger.should_receive(:<<).with("vertex_id\n")
14
14
  @edge_logger.should_receive(:<<).with("edge_id,from_id,to_id\n")
15
15
  Graphshaper::LoggingAdapter.new @vertex_logger, @edge_logger
16
16
  end
17
-
17
+
18
18
  describe "Initialized Logger" do
19
19
  before :each do
20
20
  @adapter = Graphshaper::LoggingAdapter.new @vertex_logger, @edge_logger
21
21
  end
22
-
22
+
23
23
  it "should write edges to the logger at edge creation" do
24
24
  @edge_logger.should_receive(:<<).with("0,1,3\n")
25
25
  @adapter.add_edge 0,1,3
26
26
  end
27
-
27
+
28
28
  it "should write vertices to the logger at vertex creation" do
29
29
  @vertex_logger.should_receive(:<<).with("5\n")
30
30
  @adapter.add_vertex 5
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -8,49 +8,49 @@ describe Graphshaper::SqlAdapter do
8
8
  @vertex_file = StringIO.new
9
9
  @edge_file = StringIO.new
10
10
  end
11
-
11
+
12
12
  describe "generated files" do
13
13
  before :each do
14
14
  @sql_adapter = Graphshaper::SqlAdapter.new @schema_file, @vertex_file, @edge_file
15
15
  end
16
-
16
+
17
17
  it "should write the schema to the file" do
18
18
  schema_string = @schema_file.string
19
-
19
+
20
20
  File.readlines("templates/schema.sql").each do |schema_line|
21
21
  schema_string.should include(schema_line)
22
22
  end
23
23
  end
24
-
24
+
25
25
  it "should write the vertices to the file" do
26
26
  @sql_adapter.add_vertex(0)
27
27
  @sql_adapter.add_vertex(1)
28
28
  @sql_adapter.close
29
-
29
+
30
30
  vertex_string = @vertex_file.string
31
-
31
+
32
32
  vertex_string.should include(" (1,0),\n")
33
33
  vertex_string.should include(" (2,1);\n")
34
34
  end
35
-
35
+
36
36
  it "should write the vertices to the file" do
37
37
  @sql_adapter.add_edge(0,5,6)
38
38
  @sql_adapter.add_edge(1,7,8)
39
39
  @sql_adapter.close
40
-
40
+
41
41
  edge_string = @edge_file.string
42
-
42
+
43
43
  edge_string.should include(" (1,0,6,7),\n")
44
44
  edge_string.should include(" (2,1,8,9);\n")
45
45
  end
46
46
  end
47
-
47
+
48
48
  it "should close the three files" do
49
49
  @schema_file.should_receive(:close)
50
50
  @edge_file.should_receive(:close)
51
51
  @vertex_file.should_receive(:close)
52
-
52
+
53
53
  sql_adapter = Graphshaper::SqlAdapter.new @schema_file, @vertex_file, @edge_file
54
54
  sql_adapter.close
55
55
  end
56
- end
56
+ end
@@ -7,97 +7,97 @@ describe Graphshaper::UndirectedGraph do
7
7
  graph.order.should ==(5)
8
8
  graph.size.should ==(0)
9
9
  end
10
-
10
+
11
11
  describe "initialized graph" do
12
12
  before :each do
13
13
  @graph = Graphshaper::UndirectedGraph.new 5
14
14
  end
15
-
15
+
16
16
  it "should be able to add new vertices" do
17
17
  expect { @graph.add_vertex }.to change{ @graph.order }.by(1)
18
18
  end
19
-
19
+
20
20
  it "should add a vertex with two existing ids" do
21
21
  expect { @graph.add_edge 0, 1 }.to change{ @graph.size }.by(1)
22
22
  end
23
-
23
+
24
24
  it "shouldn't add a vertex if one of the ids doesn't exist" do
25
25
  expect { @graph.add_edge 0, 5}.to raise_error(RuntimeError, "ID doesn't exist")
26
26
  end
27
-
27
+
28
28
  it "should answer the question if there is an edge between two vertices with false if they are not" do
29
29
  @graph.edge_between?(0,1).should be_false
30
30
  end
31
-
31
+
32
32
  it "should answer the question if there is an edge between two vertices with true if they are" do
33
33
  @graph.add_edge 0,1
34
34
  @graph.edge_between?(0,1).should be_true
35
35
  end
36
-
36
+
37
37
  it "shouldn't add an edge that has already been added" do
38
38
  @graph.add_edge 0,1
39
39
  expect { @graph.add_edge 0, 1 }.to change{ @graph.size }.by(0)
40
40
  end
41
-
41
+
42
42
  it "shouldn't add an edge that has already been added - independent of direction" do
43
43
  @graph.add_edge 0,1
44
44
  expect { @graph.add_edge 1,0 }.to change{ @graph.size }.by(0)
45
45
  end
46
-
46
+
47
47
  it "should not add an edge where the first and second vertex are the same" do
48
48
  expect { @graph.add_edge 0, 0}.to raise_error(RuntimeError, "No Self-Referential Edge")
49
49
  end
50
-
50
+
51
51
  it "should return the graph's order for the number of orphans for a graph without vertices" do
52
52
  @graph.number_of_orphans.should ==(@graph.order)
53
53
  end
54
-
54
+
55
55
  it "should return 0 for the number of orphans for a graph connected in a circle" do
56
56
  circle_array = (0...5).to_a
57
57
  circle_array.zip(circle_array.rotate).each do |vertex_a, vertex_b|
58
58
  @graph.add_edge vertex_a, vertex_b
59
59
  end
60
60
  end
61
-
61
+
62
62
  it "should calculate the vertex degree" do
63
63
  expect { @graph.add_edge 0,1 }.to change { @graph.vertex_degree_for 1}.by(1)
64
64
  end
65
-
65
+
66
66
  it "should calculate the degree distribution" do
67
67
  @graph.degree_distribution.should ==[5]
68
68
  @graph.add_edge 0,1
69
69
  @graph.add_edge 1,2
70
70
  @graph.degree_distribution.should ==[2,2,1]
71
71
  end
72
-
72
+
73
73
  it "should be able to connect all vertices" do
74
74
  expect { @graph.connect_all_vertices }.to change{ @graph.number_of_orphans }.by(-5)
75
75
  end
76
76
  end
77
-
77
+
78
78
  describe "calculating the vertex's degree and preferential attachment" do
79
79
  before :each do
80
80
  @graph = Graphshaper::UndirectedGraph.new 5
81
81
  end
82
-
82
+
83
83
  it "should calculate the degree of 0 for every vertex in a graph without edges" do
84
84
  5.times do |vertex_id|
85
85
  @graph.vertex_degree_for(vertex_id).should ==0
86
86
  end
87
87
  end
88
-
88
+
89
89
  it "should calculate the degree for a vertex with two edges" do
90
90
  @graph.add_edge 0,1
91
91
  @graph.add_edge 1,2
92
92
  @graph.vertex_degree_for(1).should ==2
93
93
  end
94
-
94
+
95
95
  it "should calculate the sum of all degrees" do
96
96
  @graph.add_edge 0,1
97
97
  @graph.add_edge 1,2
98
98
  @graph.sum_of_all_degrees.should ==4
99
99
  end
100
-
100
+
101
101
  it "should provide an iterator for preferential attachments that sums up to 0 for a graph without edges" do
102
102
  sum = 0
103
103
  @graph.each_vertex_with_preferential_attachment do |vertex_id, preferential_attachment|
@@ -105,7 +105,7 @@ describe Graphshaper::UndirectedGraph do
105
105
  end
106
106
  sum.should ==0
107
107
  end
108
-
108
+
109
109
  it "should calculate the preferential attachments in a way that their sum is always 1 when there is at least one edge" do
110
110
  sum = 0
111
111
  @graph.add_edge 0,1
@@ -114,7 +114,7 @@ describe Graphshaper::UndirectedGraph do
114
114
  end
115
115
  sum.should ==1
116
116
  end
117
-
117
+
118
118
  it "should add up the preferential attachments to one even if edges are added in the block" do
119
119
  sum = 0
120
120
  @graph.add_edge 0,1
@@ -124,47 +124,47 @@ describe Graphshaper::UndirectedGraph do
124
124
  end
125
125
  sum.should ==1
126
126
  end
127
-
127
+
128
128
  it "should add a vertex to the graph with edges according to preferential attachment" do
129
129
  @graph.add_edge 0,1
130
-
130
+
131
131
  # Two vertices with preferential_attachment of 0.5, all others with 0
132
132
  @graph.add_vertex do |preferential_attachment|
133
133
  preferential_attachment > 0.4
134
134
  end
135
-
135
+
136
136
  # One more vertex
137
137
  @graph.order.should ==(6)
138
-
138
+
139
139
  # Two additional edges
140
140
  @graph.size.should ==(3)
141
141
  end
142
142
  end
143
-
143
+
144
144
  describe "Adapter Support" do
145
145
  before :each do
146
146
  @adapter = double()
147
147
  @adapter.stub :add_vertex
148
148
  @adapter.stub :add_edge
149
149
  end
150
-
150
+
151
151
  it "should tell the adapter about the inital vertices on creation" do
152
152
  5.times do |vertex_id|
153
153
  @adapter.should_receive(:add_vertex).with(vertex_id)
154
154
  end
155
155
  graph = Graphshaper::UndirectedGraph.new 5, adapters: [@adapter]
156
156
  end
157
-
157
+
158
158
  it "should tell the adapter about later added vertices" do
159
159
  graph = Graphshaper::UndirectedGraph.new 5, adapters: [@adapter]
160
160
  @adapter.should_receive(:add_vertex).with(5)
161
161
  graph.add_vertex
162
162
  end
163
-
163
+
164
164
  it "should tell the adapter about later added edges" do
165
165
  graph = Graphshaper::UndirectedGraph.new 5, adapters: [@adapter]
166
166
  @adapter.should_receive(:add_edge).with(0, 1, 2)
167
167
  graph.add_edge 1, 2
168
168
  end
169
169
  end
170
- end
170
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphshaper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,63 +9,88 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-08 00:00:00.000000000Z
12
+ date: 2013-03-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
16
- requirement: &70149864493700 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 0.8.1
21
+ version: 0.10.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70149864493700
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.10.2
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rake
27
- requirement: &70149864493200 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
31
36
  - !ruby/object:Gem::Version
32
- version: 0.9.2.2
37
+ version: 10.0.3
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *70149864493200
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 10.0.3
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rspec
38
- requirement: &70149864492740 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
42
52
  - !ruby/object:Gem::Version
43
- version: 2.9.0
53
+ version: 2.13.0
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *70149864492740
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.13.0
47
62
  - !ruby/object:Gem::Dependency
48
- name: yard
49
- requirement: &70149864492280 !ruby/object:Gem::Requirement
63
+ name: webmock
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ~>
53
68
  - !ruby/object:Gem::Version
54
- version: 0.7.5
69
+ version: 1.11.0
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *70149864492280
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.11.0
58
78
  - !ruby/object:Gem::Dependency
59
- name: webmock
60
- requirement: &70149864491820 !ruby/object:Gem::Requirement
79
+ name: yard
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ~>
64
84
  - !ruby/object:Gem::Version
65
- version: 1.8.6
85
+ version: 0.8.5.2
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *70149864491820
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 0.8.5.2
69
94
  description: Generate realistic graphs
70
95
  email:
71
96
  - me@moonglum.net
@@ -83,14 +108,16 @@ files:
83
108
  - bin/graphshaper
84
109
  - graphshaper.gemspec
85
110
  - lib/graphshaper.rb
86
- - lib/graphshaper/adapters/avocadodb_adapter.rb
111
+ - lib/graphshaper/adapters/arangodb_adapter.rb
87
112
  - lib/graphshaper/adapters/dot_adapter.rb
113
+ - lib/graphshaper/adapters/json_adapter.rb
88
114
  - lib/graphshaper/adapters/logging_adapter.rb
89
115
  - lib/graphshaper/adapters/sql_adapter.rb
90
116
  - lib/graphshaper/undirected_graph.rb
91
117
  - lib/graphshaper/version.rb
92
- - spec/adapters/avocadodb_adapter_spec.rb
118
+ - spec/adapters/arangodb_adapter_spec.rb
93
119
  - spec/adapters/dot_adapter_spec.rb
120
+ - spec/adapters/json_adapter_spec.rb
94
121
  - spec/adapters/logging_adapter_spec.rb
95
122
  - spec/adapters/sql_adapter_spec.rb
96
123
  - spec/spec_helper.rb
@@ -108,21 +135,29 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
135
  - - ! '>='
109
136
  - !ruby/object:Gem::Version
110
137
  version: '0'
138
+ segments:
139
+ - 0
140
+ hash: -4051901559196272302
111
141
  required_rubygems_version: !ruby/object:Gem::Requirement
112
142
  none: false
113
143
  requirements:
114
144
  - - ! '>='
115
145
  - !ruby/object:Gem::Version
116
146
  version: '0'
147
+ segments:
148
+ - 0
149
+ hash: -4051901559196272302
117
150
  requirements: []
118
151
  rubyforge_project:
119
- rubygems_version: 1.8.6
152
+ rubygems_version: 1.8.25
120
153
  signing_key:
121
154
  specification_version: 3
122
- summary: Graphshaper can generate realistic, scale-free graphs of any size.
155
+ summary: Graphshaper can generate realistic, scale-free graphs of any size for different
156
+ databases.
123
157
  test_files:
124
- - spec/adapters/avocadodb_adapter_spec.rb
158
+ - spec/adapters/arangodb_adapter_spec.rb
125
159
  - spec/adapters/dot_adapter_spec.rb
160
+ - spec/adapters/json_adapter_spec.rb
126
161
  - spec/adapters/logging_adapter_spec.rb
127
162
  - spec/adapters/sql_adapter_spec.rb
128
163
  - spec/spec_helper.rb