graphshaper 0.2.4 → 0.3.0

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