diaspora-cluster-creator 0.2.0 → 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.
- data/.gitignore +1 -2
- data/Guardfile +1 -2
- data/README.md +2 -0
- data/TODO.todo +2 -0
- data/bin/diaspora-cluster +11 -8
- data/diaspora-cluster-creator.gemspec +2 -0
- data/features/cluster.feature +1 -1
- data/features/command_line.feature +165 -0
- data/features/node.feature +13 -0
- data/features/step_definitions/cluster_steps.rb +3 -3
- data/features/step_definitions/command_line_steps.rb +7 -0
- data/features/step_definitions/node_steps.rb +16 -0
- data/features/support/aruba.rb +1 -0
- data/features/support/world.rb +4 -8
- data/lib/diaspora-cluster-creator.rb +12 -9
- data/lib/diaspora-cluster-creator/attribute.rb +29 -0
- data/lib/diaspora-cluster-creator/attribute_collection_factory.rb +29 -0
- data/lib/diaspora-cluster-creator/cluster.rb +31 -18
- data/lib/diaspora-cluster-creator/edge_drawer.rb +41 -0
- data/lib/diaspora-cluster-creator/guarantor.rb +21 -0
- data/lib/diaspora-cluster-creator/node.rb +71 -0
- data/lib/diaspora-cluster-creator/node_attribute.rb +41 -0
- data/lib/diaspora-cluster-creator/node_collection_factory.rb +30 -0
- data/lib/diaspora-cluster-creator/template.rb +23 -16
- data/lib/diaspora-cluster-creator/version.rb +1 -1
- data/spec/diaspora-cluster-creator/attribute_collection_factory_spec.rb +31 -0
- data/spec/diaspora-cluster-creator/attribute_spec.rb +69 -0
- data/spec/diaspora-cluster-creator/cluster_spec.rb +66 -15
- data/spec/diaspora-cluster-creator/edge_drawer_spec.rb +21 -0
- data/spec/diaspora-cluster-creator/guarantor_spec.rb +54 -0
- data/spec/diaspora-cluster-creator/node_attribute_spec.rb +36 -0
- data/spec/diaspora-cluster-creator/node_collection_factory_spec.rb +27 -0
- data/spec/diaspora-cluster-creator/node_spec.rb +65 -0
- data/spec/diaspora-cluster-creator/template_integration_spec.rb +9 -10
- data/spec/diaspora-cluster-creator/template_spec.rb +9 -2
- metadata +78 -34
- data/features/star_system.feature +0 -13
- data/features/step_definitions/star_system_steps.rb +0 -16
- data/lib/diaspora-cluster-creator/graph.rb +0 -58
- data/lib/diaspora-cluster-creator/star_system.rb +0 -80
- data/spec/diaspora-cluster-creator/graph_spec.rb +0 -34
- data/spec/diaspora-cluster-creator/star_system_spec.rb +0 -103
@@ -0,0 +1,41 @@
|
|
1
|
+
module Diaspora
|
2
|
+
module Cluster
|
3
|
+
module Creator
|
4
|
+
class EdgeDrawer
|
5
|
+
extend DependencyInjector
|
6
|
+
def_injector(:dice) { FateDice.new }
|
7
|
+
attr_reader :cluster
|
8
|
+
def initialize(cluster)
|
9
|
+
@cluster = cluster
|
10
|
+
end
|
11
|
+
|
12
|
+
def draw(nodes)
|
13
|
+
return @edges if @edges
|
14
|
+
nodes.each_with_index do |node, i|
|
15
|
+
result = dice.roll
|
16
|
+
if result < 0
|
17
|
+
connect(node, nodes[i+1])
|
18
|
+
elsif result == 0
|
19
|
+
connect(node, nodes[i+1], nodes[i+2])
|
20
|
+
elsif result > 0
|
21
|
+
connect(node, nodes[i+1], nodes[i+2], nodes[i+3])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
@edges
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def connect(node, *others)
|
30
|
+
@edges ||= []
|
31
|
+
others.flatten.compact.each do |other|
|
32
|
+
@edges << [node,other]
|
33
|
+
end
|
34
|
+
@edges
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Diaspora
|
2
|
+
module Cluster
|
3
|
+
module Creator
|
4
|
+
class Guarantor
|
5
|
+
attr_reader :attribute_name, :minimum_value
|
6
|
+
def initialize(attribute_name = :technology, minimum_value = 2)
|
7
|
+
@attribute_name, @minimum_value = attribute_name, minimum_value
|
8
|
+
end
|
9
|
+
def guarantee!(nodes)
|
10
|
+
return nodes if nodes.detect {|ss| ss.send(attribute_name) >= minimum_value }
|
11
|
+
working = nodes.sort
|
12
|
+
working.first.send("#{attribute_name}=", minimum_value)
|
13
|
+
working.last.send("#{attribute_name}=", minimum_value)
|
14
|
+
nodes
|
15
|
+
rescue NoMethodError => e
|
16
|
+
nodes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'dependency_injector'
|
2
|
+
require 'set'
|
3
|
+
require_relative 'fate_dice'
|
4
|
+
require_relative 'node_attribute'
|
5
|
+
|
6
|
+
module Diaspora
|
7
|
+
module Cluster
|
8
|
+
module Creator
|
9
|
+
class Node
|
10
|
+
extend DependencyInjector
|
11
|
+
def_injector(:dice) { FateDice.new }
|
12
|
+
def_injector(:attribute_builder) { NodeAttribute.public_method(:new) }
|
13
|
+
|
14
|
+
attr_reader :cluster, :name, :attributes
|
15
|
+
def initialize(cluster, name_with_attribute_values)
|
16
|
+
@cluster = cluster
|
17
|
+
@attributes = []
|
18
|
+
yield(self) if block_given?
|
19
|
+
cluster.attributes.each do |attribute|
|
20
|
+
@attributes << attribute_builder.call(self,attribute)
|
21
|
+
end
|
22
|
+
set_name_and_attribute_values(name_with_attribute_values.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(method_name, *args, &block)
|
26
|
+
if attributes && attribute = attributes.detect {|a| a.to_sym == method_name.to_sym }
|
27
|
+
attribute.value
|
28
|
+
elsif attributes && attribute = attributes.detect {|a| "#{a.to_sym}=".to_sym == method_name.to_sym }
|
29
|
+
attribute.value = args.first
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
include Comparable
|
36
|
+
def <=>(other)
|
37
|
+
to_i <=> other.to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_i
|
41
|
+
attributes.inject(0) {|m,v| m += v.to_i}
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
name.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def label
|
49
|
+
attributes.inject("#{name}\n") {|m,attrib| m << "#{attrib.label} "}.strip
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
def set_name_and_attribute_values(name_with_attribtes)
|
54
|
+
/^(?<name>[^(?:\[|\{)]*)(?:(?:\[|\{)(?<encoded_attributes>[^(?:\]|\})]*)(?:\]|\}))?$/ =~ name_with_attribtes
|
55
|
+
@name = name.strip
|
56
|
+
extract_encoded_attributes(encoded_attributes)
|
57
|
+
end
|
58
|
+
def extract_encoded_attributes(encoded_attributes)
|
59
|
+
if encoded_attributes
|
60
|
+
encoded_attributes.split(/ +/).collect(&:strip).each do |option|
|
61
|
+
/(?<attribute_prefix>\w) *(?<attribute_value>-?\d)/ =~ option
|
62
|
+
if attribute = attributes.detect {|attrib| attrib.prefix =~ /^#{attribute_prefix}$/i}
|
63
|
+
attribute.value = attribute_value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'dependency_injector'
|
3
|
+
require_relative 'fate_dice'
|
4
|
+
require_relative 'attribute'
|
5
|
+
|
6
|
+
module Diaspora
|
7
|
+
module Cluster
|
8
|
+
module Creator
|
9
|
+
class NodeAttribute < DelegateClass(Attribute)
|
10
|
+
extend DependencyInjector
|
11
|
+
def_injector(:dice) { FateDice.new }
|
12
|
+
|
13
|
+
attr_reader :node
|
14
|
+
def initialize(node, attribute)
|
15
|
+
@node = node
|
16
|
+
super(attribute)
|
17
|
+
end
|
18
|
+
|
19
|
+
def value
|
20
|
+
@value ||= dice.roll
|
21
|
+
end
|
22
|
+
|
23
|
+
def value=(new_value)
|
24
|
+
@value = new_value.to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_i
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"#{super}#{to_i}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def label
|
36
|
+
"#{super}#{to_i}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'dependency_injector'
|
2
|
+
require_relative 'node'
|
3
|
+
require_relative 'guarantor'
|
4
|
+
|
5
|
+
module Diaspora
|
6
|
+
module Cluster
|
7
|
+
module Creator
|
8
|
+
class NodeCollectionFactory
|
9
|
+
extend DependencyInjector
|
10
|
+
def_injector(:node_builder) { Node.public_method(:new) }
|
11
|
+
def_injector(:node_guarantor) { Guarantor.new(:technology, 2).public_method(:guarantee!) }
|
12
|
+
|
13
|
+
attr_reader :cluster
|
14
|
+
def initialize(cluster)
|
15
|
+
@cluster = cluster
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_from(names)
|
19
|
+
collection = []
|
20
|
+
names.each_with_object(collection) do |name,mem|
|
21
|
+
mem << node_builder.call(cluster, name)
|
22
|
+
end
|
23
|
+
node_guarantor.call(collection)
|
24
|
+
collection
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -3,36 +3,43 @@ module Diaspora
|
|
3
3
|
module Cluster
|
4
4
|
module Creator
|
5
5
|
class Template
|
6
|
-
attr_reader :
|
7
|
-
def initialize(
|
8
|
-
raise RuntimeError unless
|
9
|
-
raise RuntimeError unless
|
10
|
-
@
|
6
|
+
attr_reader :cluster
|
7
|
+
def initialize(cluster)
|
8
|
+
raise RuntimeError unless cluster.respond_to?(:each_node)
|
9
|
+
raise RuntimeError unless cluster.respond_to?(:each_edge)
|
10
|
+
@cluster = cluster
|
11
11
|
end
|
12
|
+
|
12
13
|
def to_s
|
13
14
|
canvas.to_s
|
14
15
|
end
|
15
|
-
def to_dot(filename = '
|
16
|
+
def to_dot(filename = 'cluster.dot')
|
16
17
|
canvas.output(:dot => "#{filename}")
|
17
18
|
end
|
18
|
-
def to_png(filename = '
|
19
|
+
def to_png(filename = 'cluster.png')
|
19
20
|
canvas.output(:png => "#{filename}")
|
20
21
|
end
|
22
|
+
def to_svg(filename = 'cluster.svg')
|
23
|
+
canvas.output(:svg => "#{filename}")
|
24
|
+
end
|
21
25
|
protected
|
22
26
|
def canvas
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
return @canvas if @canvas
|
28
|
+
@canvas = GraphViz.new(cluster.to_s, :type => :graph )
|
29
|
+
|
30
|
+
cluster.each_node do |node|
|
31
|
+
options = {}
|
32
|
+
options[:label] = (node.respond_to?(:label) ? node.label : node.to_s)
|
33
|
+
@canvas.add_nodes(node.to_s, options)
|
27
34
|
end
|
28
|
-
|
29
|
-
|
35
|
+
|
36
|
+
cluster.each_edge do |edge|
|
30
37
|
to = edge[0]
|
31
38
|
from = edge[1]
|
32
|
-
|
39
|
+
@canvas.add_edges(to.to_s, from.to_s)
|
33
40
|
end
|
34
|
-
|
35
|
-
|
41
|
+
|
42
|
+
@canvas
|
36
43
|
end
|
37
44
|
end
|
38
45
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative '../spec_helper_lite'
|
2
|
+
require 'attribute_collection_factory'
|
3
|
+
|
4
|
+
describe AttributeCollectionFactory do
|
5
|
+
subject { AttributeCollectionFactory.new(cluster)}
|
6
|
+
let(:cluster) { Object.new }
|
7
|
+
it 'should extract attributes from name' do
|
8
|
+
subject.attribute_builder = lambda {|string|
|
9
|
+
"attr-#{string}"
|
10
|
+
}
|
11
|
+
subject.build_from(['Magic','Trees','Cows']).must_equal(["attr-Magic", "attr-Trees", "attr-Cows"])
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should build from default when empty array is sent' do
|
15
|
+
attribute_builder = lambda {|string|
|
16
|
+
"attr-#{string}"
|
17
|
+
}
|
18
|
+
subject.attribute_builder = attribute_builder
|
19
|
+
expected = AttributeCollectionFactory::DEFAULT_ATTRIBUTE_NAMES.collect {|name| attribute_builder.call(name)}
|
20
|
+
subject.build_from([]).must_equal(expected)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should build from default when nil is sent' do
|
24
|
+
attribute_builder = lambda {|string|
|
25
|
+
"attr-#{string}"
|
26
|
+
}
|
27
|
+
subject.attribute_builder = attribute_builder
|
28
|
+
expected = AttributeCollectionFactory::DEFAULT_ATTRIBUTE_NAMES.collect {|name| attribute_builder.call(name)}
|
29
|
+
subject.build_from(nil).must_equal(expected)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../spec_helper_lite'
|
2
|
+
require 'attribute'
|
3
|
+
|
4
|
+
describe Attribute do
|
5
|
+
describe 'with assumed prefix' do
|
6
|
+
subject { Attribute.new('Hair Color')}
|
7
|
+
|
8
|
+
describe '#to_s' do
|
9
|
+
it 'should use the name' do
|
10
|
+
subject.to_s.must_equal 'Hair Color'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#to_sym' do
|
15
|
+
it 'should convert name to method friendly sym' do
|
16
|
+
subject.to_sym.must_equal :hair_color
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#name' do
|
21
|
+
it 'should have a name' do
|
22
|
+
subject.name.must_equal 'Hair Color'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
describe '#prefix' do
|
26
|
+
it 'should default to first character' do
|
27
|
+
subject.prefix.must_equal 'H'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#label' do
|
32
|
+
it 'should be the prefix' do
|
33
|
+
subject.label.must_equal(subject.prefix)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'with extracted prefix' do
|
39
|
+
subject { Attribute.new('H(a)ir Color')}
|
40
|
+
|
41
|
+
describe '#to_s' do
|
42
|
+
it 'should use the name' do
|
43
|
+
subject.to_s.must_equal 'Hair Color'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#to_sym' do
|
48
|
+
it 'should convert name to method friendly sym' do
|
49
|
+
subject.to_sym.must_equal :hair_color
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#name' do
|
54
|
+
it 'should have a name' do
|
55
|
+
subject.name.must_equal 'Hair Color'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
describe '#prefix' do
|
59
|
+
it 'should be extracted' do
|
60
|
+
subject.prefix.must_equal 'A'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
describe '#label' do
|
64
|
+
it 'should be the prefix' do
|
65
|
+
subject.label.must_equal(subject.prefix)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -2,25 +2,76 @@ require_relative '../spec_helper_lite'
|
|
2
2
|
require 'cluster'
|
3
3
|
|
4
4
|
describe Cluster do
|
5
|
-
subject { Cluster.new(names) }
|
5
|
+
subject { Cluster.new(names, attribute_names) }
|
6
|
+
let(:attribute_names) { ['tech'] }
|
6
7
|
let(:names) { ['a'] }
|
7
8
|
|
8
|
-
describe '#
|
9
|
-
it 'should yield
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
describe '#each_node' do
|
10
|
+
it 'should yield the nodes' do
|
11
|
+
expected_output = [1,2,3]
|
12
|
+
subject.node_collection_builder = lambda { expected_output }
|
13
|
+
@yielded = []
|
14
|
+
subject.each_node do |node|
|
15
|
+
@yielded << node
|
16
|
+
end
|
17
|
+
@yielded.must_equal(expected_output)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#nodes' do
|
22
|
+
it 'should delegate node creation to node_maker' do
|
23
|
+
expected_output = ['output']
|
24
|
+
maker = MiniTest::Mock.new
|
25
|
+
maker.expect(:call, expected_output)
|
26
|
+
subject.node_collection_builder = maker
|
27
|
+
subject.nodes.must_equal(expected_output)
|
28
|
+
maker.verify
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#each_edge' do
|
33
|
+
it 'should yield the edges' do
|
34
|
+
expected_output = [1,2,3]
|
35
|
+
subject.edge_collection_builder = lambda { expected_output }
|
19
36
|
@yielded = []
|
20
|
-
subject.
|
37
|
+
subject.each_edge do |edge|
|
38
|
+
@yielded << edge
|
39
|
+
end
|
40
|
+
@yielded.must_equal(expected_output)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#edges' do
|
45
|
+
it 'should delegate edge drawing to the edge_collection_builder' do
|
46
|
+
expected_output = ['output']
|
47
|
+
drawer = MiniTest::Mock.new
|
48
|
+
drawer.expect(:call, expected_output)
|
49
|
+
subject.edge_collection_builder = drawer
|
50
|
+
subject.edges.must_equal(expected_output)
|
51
|
+
drawer.verify
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#each_attribute' do
|
56
|
+
it 'should yield the attributes' do
|
57
|
+
expected_output = [1,2,3]
|
58
|
+
subject.attribute_collection_builder = lambda { expected_output }
|
59
|
+
@yielded = []
|
60
|
+
subject.each_attribute do |attribute|
|
61
|
+
@yielded << attribute
|
62
|
+
end
|
63
|
+
@yielded.must_equal(expected_output)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#attributes' do
|
68
|
+
it 'should delegate attribute creation to the attribute_collection_builder' do
|
69
|
+
expected_output = ['output']
|
70
|
+
builder = MiniTest::Mock.new
|
71
|
+
builder.expect(:call, expected_output)
|
72
|
+
subject.attribute_collection_builder = builder
|
73
|
+
subject.attributes.must_equal(expected_output)
|
21
74
|
builder.verify
|
22
|
-
guarantor.verify
|
23
|
-
@yielded.must_equal [transformed_object]
|
24
75
|
end
|
25
76
|
end
|
26
77
|
end
|