| Class | Puppet::SimpleGraph |
| In: |
lib/puppet/simple_graph.rb
|
| Parent: | Object |
A hopefully-faster graph class to replace the use of GRATR.
# File lib/puppet/simple_graph.rb, line 85
85: def initialize
86: @vertices = {}
87: @edges = []
88: end
Add a new edge. The graph user has to create the edge instance, since they have to specify what kind of edge it is.
# File lib/puppet/simple_graph.rb, line 182
182: def add_edge(source, target = nil, label = nil)
183: if target
184: edge = Puppet::Relationship.new(source, target, label)
185: else
186: edge = source
187: end
188: [edge.source, edge.target].each { |vertex| setup_vertex(vertex) unless vertex?(vertex) }
189: @vertices[edge.source].add_edge :out, edge
190: @vertices[edge.target].add_edge :in, edge
191: @edges << edge
192: true
193: end
Clear our graph.
# File lib/puppet/simple_graph.rb, line 91
91: def clear
92: @vertices.each { |vertex, wrapper| wrapper.clear }
93: @vertices.clear
94: @edges.clear
95: end
Whether our graph is directed. Always true. Used to produce dot files.
# File lib/puppet/simple_graph.rb, line 98
98: def directed?
99: true
100: end
Find a matching edge. Note that this only finds the first edge, not all of them or whatever.
# File lib/puppet/simple_graph.rb, line 197
197: def edge(source, target)
198: @edges.each_with_index { |test_edge, index| return test_edge if test_edge.source == source and test_edge.target == target }
199: end
# File lib/puppet/simple_graph.rb, line 201
201: def edge_label(source, target)
202: return nil unless edge = edge(source, target)
203: edge.label
204: end
Remove an edge from our graph.
# File lib/puppet/simple_graph.rb, line 218
218: def remove_edge!(edge)
219: @vertices[edge.source].remove_edge(:out, edge)
220: @vertices[edge.target].remove_edge(:in, edge)
221:
222: # Here we are looking for an exact edge, so we don't want to use ==, because
223: # it's too darn expensive (in testing, deleting 3000 edges went from 6 seconds to
224: # 0.05 seconds with this change).
225: @edges.each_with_index { |test_edge, index| @edges.delete_at(index) and break if edge.equal?(test_edge) }
226: nil
227: end
Remove a vertex from the graph.
# File lib/puppet/simple_graph.rb, line 163
163: def remove_vertex!(vertex)
164: return nil unless vertex?(vertex)
165: @vertices[vertex].edges.each { |edge| remove_edge!(edge) }
166: @vertices[vertex].clear
167: @vertices.delete(vertex)
168: end
Return a reversed version of this graph.
# File lib/puppet/simple_graph.rb, line 103
103: def reversal
104: result = self.class.new
105: vertices.each { |vertex| result.add_vertex(vertex) }
106: edges.each do |edge|
107: newedge = edge.class.new(edge.target, edge.source, edge.label)
108: result.add_edge(newedge)
109: end
110: result
111: end
Return the graph as an array.
# File lib/puppet/simple_graph.rb, line 119
119: def to_a
120: @vertices.keys
121: end
Output the dot format as a string
# File lib/puppet/simple_graph.rb, line 298
298: def to_dot (params={}) to_dot_graph(params).to_s; end
Return a DOT::DOTDigraph for directed graphs or a DOT::DOTSubgraph for an undirected Graph. params can contain any graph property specified in rdot.rb. If an edge or vertex label is a kind of Hash then the keys which match dot properties will be used as well.
# File lib/puppet/simple_graph.rb, line 272
272: def to_dot_graph (params = {})
273: params['name'] ||= self.class.name.gsub(/:/,'_')
274: fontsize = params['fontsize'] ? params['fontsize'] : '8'
275: graph = (directed? ? DOT::DOTDigraph : DOT::DOTSubgraph).new(params)
276: edge_klass = directed? ? DOT::DOTDirectedEdge : DOT::DOTEdge
277: vertices.each do |v|
278: name = v.to_s
279: params = {'name' => '"'+name+'"',
280: 'fontsize' => fontsize,
281: 'label' => name}
282: v_label = v.to_s
283: params.merge!(v_label) if v_label and v_label.kind_of? Hash
284: graph << DOT::DOTNode.new(params)
285: end
286: edges.each do |e|
287: params = {'from' => '"'+ e.source.to_s + '"',
288: 'to' => '"'+ e.target.to_s + '"',
289: 'fontsize' => fontsize }
290: e_label = e.to_s
291: params.merge!(e_label) if e_label and e_label.kind_of? Hash
292: graph << edge_klass.new(params)
293: end
294: graph
295: end
# For some reason, unconnected vertices do not show up in # this graph. def to_jpg(path, name)
gv = vertices()
Dir.chdir(path) do
induced_subgraph(gv).write_to_graphic_file('jpg', name)
end
end
# File lib/puppet/simple_graph.rb, line 254
254: def to_yaml_properties
255: instance_variables
256: end
Provide a topological sort.
# File lib/puppet/simple_graph.rb, line 124
124: def topsort
125: degree = {}
126: zeros = []
127: result = []
128:
129: # Collect each of our vertices, with the number of in-edges each has.
130: @vertices.each do |name, wrapper|
131: edges = wrapper.in_edges
132: zeros << wrapper if edges.length == 0
133: degree[wrapper.vertex] = edges
134: end
135:
136: # Iterate over each 0-degree vertex, decrementing the degree of
137: # each of its out-edges.
138: while wrapper = zeros.pop do
139: result << wrapper.vertex
140: wrapper.out_edges.each do |edge|
141: degree[edge.target].delete(edge)
142: zeros << @vertices[edge.target] if degree[edge.target].length == 0
143: end
144: end
145:
146: # If we have any vertices left with non-zero in-degrees, then we've found a cycle.
147: if cycles = degree.find_all { |vertex, edges| edges.length > 0 } and cycles.length > 0
148: message = cycles.collect { |vertex, edges| edges.collect { |e| e.to_s }.join(", ") }.join(", ")
149: raise Puppet::Error, "Found dependency cycles in the following relationships: %s" % message
150: end
151:
152: return result
153: end
Test whether a given vertex is in the graph.
# File lib/puppet/simple_graph.rb, line 171
171: def vertex?(vertex)
172: @vertices.include?(vertex)
173: end
Use dot to create a graphical representation of the graph. Returns the filename of the graphics file.
# File lib/puppet/simple_graph.rb, line 309
309: def write_to_graphic_file (fmt='png', dotfile='graph')
310: src = dotfile + '.dot'
311: dot = dotfile + '.' + fmt
312:
313: File.open(src, 'w') {|f| f << self.to_dot << "\n"}
314:
315: system( "dot -T#{fmt} #{src} -o #{dot}" )
316: dot
317: end