gexf 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,52 @@
1
+ class GEXF::Node
2
+ extend Forwardable
3
+ include GEXF::Attribute::Assignable
4
+
5
+ attr_reader :id, :label
6
+
7
+ def_delegators :@graph, :edgetype
8
+
9
+ def initialize(id, graph, opts={})
10
+ @graph = graph
11
+ @id = id_type(id)
12
+ @label = opts[:label] || @id
13
+
14
+ # see GEXF::Attribute::Assignable
15
+ @collection = graph.nodes
16
+ @attr_values = {}
17
+
18
+ set_attributes(opts.fetch(:attributes, {}))
19
+ end
20
+
21
+ def connect_to(target, opts={})
22
+ graph.nodes << target
23
+ graph.create_edge(self, target, opts)
24
+ end
25
+
26
+ def create_and_connect_to(target_attr, opts={})
27
+ node = graph.create_node(target_attr)
28
+ connect_to(node, opts)
29
+ node
30
+ end
31
+
32
+ def connections(type=nil)
33
+ graph.edges[self.id].to_a
34
+ end
35
+
36
+ def to_hash
37
+ {:id => id, :label => label}
38
+ end
39
+
40
+ private
41
+ def set_attributes(attributes={})
42
+ attributes.each { |attr,value| self[attr]=value }
43
+ end
44
+
45
+ def id_type(id)
46
+ graph.idtype == GEXF::Graph::INTEGER ? id.to_i : id.to_s
47
+ end
48
+
49
+ def graph
50
+ @graph
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ class GEXF::NodeSet < Set
2
+ extend Forwardable
3
+ include GEXF::Attribute::Definable
4
+
5
+ def_delegators :@hash, :[], :fetch, :keys, :values
6
+
7
+ def <<(node)
8
+ @hash[node.id] = node if node.is_a?(GEXF::Node)
9
+ end
10
+
11
+ def each(&block)
12
+ @hash.values.each(&block)
13
+ end
14
+
15
+ def inspect
16
+ "<GEXF::NodeSet #{@hash.keys}>"
17
+ end
18
+
19
+ end
@@ -0,0 +1,38 @@
1
+ class GEXF::SetOfSets < Set
2
+ extend ::Forwardable
3
+ include GEXF::Attribute::Definable
4
+
5
+ def_delegators :@hash, :empty?
6
+
7
+ def [](node_id)
8
+ @hash[node_id]
9
+ end
10
+
11
+ def to_a
12
+ uniq_items
13
+ end
14
+
15
+ def to_hash
16
+ @hash
17
+ end
18
+
19
+ def each(&block)
20
+ uniq_items.each(&block)
21
+ end
22
+
23
+ [:size, :count, :length].each do |method|
24
+ define_method(method) do
25
+ uniq_items.count
26
+ end
27
+ end
28
+
29
+ private
30
+ def append_to_key(key, value)
31
+ @hash[key] ||= Set.new
32
+ @hash[key] << value
33
+ end
34
+
35
+ def uniq_items
36
+ @hash.map {| _, v| v.to_a }.flatten.uniq
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ GEXF::GRAPH_TYPES = (GEXF::Attribute::TYPES +
2
+ GEXF::Edge::TYPES +
3
+ GEXF::Graph::IDTYPES +
4
+ GEXF::Graph::MODES).uniq
5
+
6
+ class Hash
7
+ def symbolize_keys
8
+ hash = {}
9
+ each { |k,v| hash[k.to_sym] = delete(k) }
10
+ merge(hash)
11
+ end
12
+
13
+ def symbolize_graph_types
14
+ hash = {}
15
+ each { |k, v| hash[k] = delete(k).to_sym if GEXF::GRAPH_TYPES.include?(v.to_sym) }
16
+ merge(hash)
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module GEXF
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,93 @@
1
+ class GEXF::XmlSerializer
2
+
3
+ GEXF_ATTRS = {
4
+ 'xmlns' => '"http://www.gexf.net/1.2draft',
5
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
6
+ 'xsi' => 'http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd',
7
+ 'version' => '1.2'
8
+ }
9
+
10
+ def initialize(graph)
11
+ @graph = graph
12
+ @document = nil
13
+ end
14
+
15
+ def serialize!
16
+ document.to_xml
17
+ end
18
+
19
+ private
20
+ def g
21
+ @graph
22
+ end
23
+
24
+ def graph_attributes
25
+ { :defaultedgetype => g.defaultedgetype,
26
+ :idtype => g.idtype,
27
+ :mode => g.mode }
28
+ end
29
+
30
+ def build_attributes(xml)
31
+ %w(nodes edges).each do |type|
32
+ defined_attributes = g.send(type).defined_attributes
33
+ next if defined_attributes.empty?
34
+
35
+ xml.attributes(:class => type.gsub(/s$/,'')) {
36
+ defined_attributes.map do |id, attr|
37
+ xml.attribute(attr.to_hash) {
38
+ xml.default(attr.default) if attr.default
39
+ }
40
+ end
41
+ }
42
+ end
43
+ end
44
+
45
+ def build_nodes(xml)
46
+ build_collection(xml, 'nodes')
47
+ end
48
+
49
+ def build_edges(xml)
50
+ build_collection(xml, 'edges')
51
+ end
52
+
53
+ def build_collection(xml, collection_name, tagname=nil)
54
+ tagname ||= collection_name.gsub /s$/,''
55
+
56
+ xml.send(collection_name) do
57
+ g.send(collection_name).each do |item|
58
+ build_item(xml, item, tagname)
59
+ end
60
+ end
61
+ end
62
+
63
+ def build_item(xml, item, tagname)
64
+ if item.attr_values.any?
65
+ xml.send(tagname, item.to_hash) do
66
+ xml.attvalues do
67
+ item.attr_values.each do |id, value|
68
+ value = value.join('|') if value.respond_to?(:join)
69
+ xml.attvalue(:for => id, :value => value)
70
+ end
71
+ end
72
+ end
73
+ else
74
+ xml.send(tagname, item.to_hash)
75
+ end
76
+ end
77
+
78
+ def document
79
+ @document ||= build do |xml|
80
+ xml.gexf(GEXF_ATTRS) do
81
+ xml.graph(graph_attributes) do
82
+ build_attributes(xml)
83
+ build_nodes(xml)
84
+ build_edges(xml)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def build(&block)
91
+ Nokogiri::XML::Builder.new(:encoding => 'UTF-8', &block)
92
+ end
93
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ module FakeClasses
4
+ class MyClass
5
+ include GEXF::Attribute::Assignable
6
+
7
+ def initialize(collection, attr_values)
8
+ @collection = collection
9
+ @attr_values = attr_values
10
+ end
11
+ end
12
+ end
13
+
14
+ describe GEXF::Attribute::Assignable do
15
+
16
+ let(:attribute1) { double('attribute1', :id => '1', :title => "attribute1", :default => nil) }
17
+ let(:attribute2) { double('attribute2', :id => '2', :title => "attribute2", :default => nil) }
18
+ let(:attribute3) { double('attribute3', :id => '3', :title => "attribute3", :default => nil) }
19
+ let(:frog) { GEXF::Attribute.new(4, 'frog', :default => true, :type => GEXF::Attribute::BOOLEAN) }
20
+
21
+ let(:attr_values) {{}}
22
+
23
+ let(:attributes) { { '1' => attribute1,
24
+ '2' => attribute2,
25
+ '3' => attribute3 } }
26
+
27
+ let(:collection) { double('collection', :attribute_definitions => attributes) }
28
+
29
+ subject { FakeClasses::MyClass.new(collection, attr_values) }
30
+
31
+ describe "#attributes" do
32
+ context "when graph has no attribute definition" do
33
+ let(:attributes) {{}}
34
+ its(:attributes) { should == attributes }
35
+ end
36
+
37
+ context "when graph has attributes" do
38
+ context "node/edge has no values set" do
39
+ it "returns a hash of attributes with nil values" do
40
+ subject.attributes.should == {
41
+ 'attribute1' => nil,
42
+ 'attribute2' => nil,
43
+ 'attribute3' => nil
44
+ }
45
+ end
46
+ end
47
+
48
+ context "node/edge has values" do
49
+
50
+ let(:attr_values) {{ '1' => 'foo',
51
+ '2' => 'bar'}}
52
+
53
+ it "returns a hash of attributes with non-nil values" do
54
+ subject.attributes.should == {
55
+ 'attribute1' => 'foo',
56
+ 'attribute2' => 'bar',
57
+ 'attribute3' => nil
58
+ }
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#[]" do
65
+ context 'when attribute title does not exist' do
66
+ it "raises a warning, and returns nil" do
67
+ Kernel.should_receive(:warn).with("undefined attribute 'other-attribute'")
68
+ subject['other-attribute'].should be_nil
69
+ end
70
+ end
71
+ context "when node/edge has no attribute values" do
72
+ it "returns nil" do
73
+ Kernel.should_not_receive(:warn)
74
+ subject['attribute1'].should be_nil
75
+ end
76
+ end
77
+
78
+ context "when node/edge has a value" do
79
+ let(:attr_values) {{ '1' => 'foo' }}
80
+
81
+ it "returns the value" do
82
+ subject[:attribute1].should == 'foo'
83
+ end
84
+ end
85
+
86
+ context "when attribute has a default value" do
87
+ let(:attributes) {{ frog.id => frog }}
88
+
89
+ it "returns the default value" do
90
+ subject[:frog].should == true
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "#[]=" do
96
+
97
+ let(:value) { '22' }
98
+
99
+ context 'when attribute title does not exist' do
100
+ it "prints a warning, does not set the attribute value" do
101
+ Kernel.should_receive(:warn).with("undefined attribute 'other-attribute'")
102
+ attr_values.should_not_receive(:[]=)
103
+ subject['other-attribute'] = value
104
+ end
105
+ end
106
+
107
+ context "when attribute title does exist" do
108
+ it "sets the attribute value" do
109
+ Kernel.should_not_receive(:warn)
110
+ attribute2.should_receive(:coherce).with(value).and_return(value)
111
+ attribute2.should_receive(:is_valid?).with(value).and_return(true)
112
+ attr_values.should_receive(:[]=).with('2', value)
113
+ subject['attribute2'] = value
114
+ end
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ module FakeClassess
4
+ class NodeSet < Set
5
+ include GEXF::Attribute::Definable
6
+ end
7
+ end
8
+
9
+ describe GEXF::Attribute::Definable do
10
+ let(:node1) { mock('node', :id => '1' )}
11
+ let(:node2) { mock('node', :id => '2' )}
12
+ let(:nodeset) { FakeClassess::NodeSet.new }
13
+ let(:attr_id) { '22' }
14
+ let(:title) { 'tags' }
15
+ let(:attr_type) { GEXF::Attribute::LIST_STRING }
16
+ let(:opts) {{ :type => attr_type }}
17
+ let(:arguments) {[attr_id, title, opts]}
18
+
19
+ before do
20
+ nodeset << node1 << node2
21
+ end
22
+
23
+ describe "#define_attribute" do
24
+ subject { nodeset.define_attribute(*arguments) }
25
+
26
+ it "Instantiates an new GEXF::Attribute" do
27
+ subject.should be_a_kind_of(GEXF::Attribute)
28
+ end
29
+ end
30
+
31
+ describe "#attribute_definitions" do
32
+
33
+ let(:attribute) { mock('attribute') }
34
+
35
+ subject { nodeset }
36
+
37
+ before(:each) do
38
+ GEXF::Attribute.should_receive(:new).and_return(attribute)
39
+ nodeset.define_attribute(*arguments)
40
+ end
41
+
42
+ its(:attribute_definitions) { should have_key(attr_id) }
43
+ its(:attribute_definitions) { should have_value(attribute) }
44
+ its(:attribute_definitions) { should have(1).item }
45
+ end
46
+
47
+ describe "#attributes" do
48
+ let(:nodeattrs) {{ :foo => 'fooval', :bar => 'barval' }}
49
+
50
+ subject { nodeset.attributes }
51
+
52
+ before(:each) do
53
+ node1.stub(:attributes).and_return(nil)
54
+ node2.stub(:attributes).and_return(nodeattrs)
55
+
56
+ nodeset.define_attribute('1', 'foo')
57
+ nodeset.define_attribute('2', 'bar')
58
+ end
59
+
60
+ it "returns a hash of items with attributes" do
61
+ subject.should_not have_key('1')
62
+ subject.should have_key('2')
63
+ subject.should have_value(nodeattrs)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ TRUTHY_INPUTS = [1, '1', true, 'true']
4
+ FALSY_INPUTS = [0, '0', false, 'false']
5
+ EMPTY_ARRAY = []
6
+ EMPTY_STRING = []
7
+ OBJECT = Object.new
8
+
9
+ describe GEXF::Attribute do
10
+
11
+ let(:id) { 22 }
12
+ let(:type) { nil }
13
+ let(:mode) { nil }
14
+ let(:attr_class) { nil }
15
+ let(:title) { 'foo' }
16
+ let(:attr_options) { ['foo', 'bar', 'baz'] }
17
+ let(:title) { 'bar' }
18
+ let(:default) { 'foo' }
19
+
20
+ let(:options) {{ :mode => mode,
21
+ :class => attr_class,
22
+ :default => default,
23
+ :type => type,
24
+ :options => attr_options }}
25
+
26
+ let(:arguments) { [id, title, options] }
27
+
28
+ subject { GEXF::Attribute.new(*arguments) }
29
+
30
+ describe "getter methods" do
31
+ its(:title) { should == 'bar' }
32
+ its(:type) { should == GEXF::Attribute::STRING }
33
+ its(:id) { should == id.to_s }
34
+ its(:options) { should == attr_options }
35
+ its(:default) { should == default }
36
+ its(:mode) { should == GEXF::Attribute::STATIC }
37
+ its(:attr_class) { should == GEXF::Attribute::NODE }
38
+
39
+ context "with invalid type" do
40
+ let(:type) { :BANANA }
41
+
42
+ it "raises an ArgumentError" do
43
+ expect { subject }.to raise_error(ArgumentError)
44
+ end
45
+ end
46
+
47
+ {:mode => :FOO, :attr_class => :BANANA}.each do |attr, invalid_value|
48
+ context "when :#{attr} is not valid" do
49
+ let(attr) { invalid_value }
50
+
51
+ it "raises an ArgumentError" do
52
+ expect { subject }.to raise_error(ArgumentError)
53
+ end
54
+ end
55
+ end
56
+
57
+ context "when 'options' do not include 'default' value" do
58
+ let(:default) { :bacon }
59
+
60
+ it "raises an ArgumentError" do
61
+ expect { subject }.to raise_error(ArgumentError)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "is_valid?" do
67
+ context "when :options is empty/nil" do
68
+ let(:attr_options) { nil }
69
+
70
+ it "returns true" do
71
+ subject.is_valid?('whaterver').should be_true
72
+ end
73
+ end
74
+
75
+ context "when :options is not empty" do
76
+ context "and :options include input value" do
77
+ it "returns true" do
78
+ subject.is_valid?('foo').should be_true
79
+ end
80
+ end
81
+ context "and :options do not include input value" do
82
+ it "returns false" do
83
+ subject.is_valid?('bacon').should be_false
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "coherce" do
90
+
91
+ subject { GEXF::Attribute.new(*arguments).coherce(value) }
92
+
93
+ context "when attribute type is :BOOLEAN" do
94
+ let(:type) { GEXF::Attribute::BOOLEAN }
95
+
96
+ context "Falsy inputs" do
97
+ FALSY_INPUTS.each do |value|
98
+ context "and input value is #{value}" do
99
+ let(:value) { value }
100
+ it { subject.should be_false }
101
+ end
102
+ end
103
+ end
104
+ context "Truty inputs" do
105
+ TRUTHY_INPUTS.each do |value|
106
+ context "and input value is #{value}" do
107
+ let(:value) { value }
108
+ it { subject.should be_true }
109
+ end
110
+ end
111
+ end
112
+ context "other input types" do
113
+ [22, -2, Object.new, [1,2,3], 'hello', :hi].each do |value|
114
+ context "input value is '#{value.inspect}'" do
115
+ let(:value) { value }
116
+
117
+ it { subject.should be_nil }
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ [GEXF::Attribute::STRING, GEXF::Attribute::ANY_URI].each do |type|
124
+ context "when attribute type is #{type}" do
125
+ let(:type) { type }
126
+
127
+ [22, 2.1, Object.new, nil, true, false].each do |value|
128
+ context "and input value is '#{value.inspect}'" do
129
+ let(:value) { value }
130
+ it { should == value.to_s }
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ context "when attribute type is :INTEGER" do
137
+ let(:type) { GEXF::Attribute::INTEGER }
138
+
139
+ [22, 2.1, nil].each do |value|
140
+ context "and input value is '#{value.inspect}'" do
141
+ let(:value) { value }
142
+ it { should == value.to_i }
143
+ end
144
+ end
145
+
146
+ [Object.new, true, false].each do |value|
147
+ context "and input value is '#{value.inspect}'" do
148
+ let(:value) { value }
149
+ it { should be_nil }
150
+ end
151
+ end
152
+ end
153
+
154
+ context "when attribute type is :LIST_STRING" do
155
+ let(:type) { GEXF::Attribute::LIST_STRING }
156
+
157
+ context "input is nil" do
158
+ let(:value) { nil }
159
+
160
+ it { subject.should == [] }
161
+ end
162
+
163
+ context "input is not empty" do
164
+ let(:value) { [:foo, :bar, 22] }
165
+ it { subject.should == value.map(&:to_s) }
166
+ end
167
+
168
+ context "input contains duplicates" do
169
+ let(:value) { [:foo, :bar, 22, 'foo'] }
170
+ it { subject.should == %w(foo bar 22) }
171
+ end
172
+
173
+ context "input contains nested lists" do
174
+ let(:value) { [:foo, :bar, 22, ['foo']] }
175
+ it { subject.should == %w(foo bar 22) }
176
+ end
177
+ end
178
+ end
179
+ end