Class: Graph

Inherits:
Object
  • Object
show all
Defined in:
lib/graph.rb

Overview

Graph models directed graphs and subgraphs and outputs in graphviz’s dot format.

Defined Under Namespace

Classes: Attribute, CompoundAttribute, Edge, Node, Thingy

Constant Summary collapse

VERSION =

:nodoc:

"2.3.1"
LIGHT_COLORS =
%w(gray lightblue lightcyan lightgray lightpink
lightslategray lightsteelblue white)
BOLD_COLORS =

WTF – can’t be %w() because of a bug in rcov

["black", "brown", "mediumblue", "blueviolet",
"orange", "magenta", "darkgreen", "maroon",
"violetred", "purple", "greenyellow", "deeppink",
"midnightblue", "firebrick", "darkturquoise",
"mediumspringgreen", "chartreuse", "navy",
"lightseagreen", "chocolate", "lawngreen", "green",
"indigo", "darkgoldenrod", "darkviolet", "red",
"springgreen", "saddlebrown", "mediumvioletred",
"goldenrod", "tomato", "cyan", "forestgreen",
"darkorchid", "crimson", "coral", "deepskyblue",
"seagreen", "peru", "turquoise", "orangered",
"dodgerblue", "sienna", "limegreen", "royalblue",
"darkorange", "blue"]
COLOR_SCHEME_MAX =

Defines the brewer color schemes and the maximum number of colors in each set.

{
  :accent   => 8,  :blues    => 9,  :brbg     => 11, :bugn     => 9,
  :dark2    => 8,  :gnbu     => 9,  :greens   => 9,  :greys    => 9,
  :oranges  => 9,  :orrd     => 9,  :paired   => 12, :pastel1  => 9,
  :pastel2  => 8,  :piyg     => 11, :prgn     => 11, :pubu     => 9,
  :pubugn   => 9,  :puor     => 11, :purd     => 9,  :purples  => 9,
  :rdbu     => 11, :rdgy     => 11, :rdylbu   => 11, :rdylgn   => 11,
  :reds     => 9,  :set1     => 9,  :set2     => 8,  :set3     => 12,
  :spectral => 11, :ylgn     => 9,  :ylgnbu   => 9,  :ylorbr   => 9,
  :ylorrd   => 9
}
SHAPES =
%w(Mcircle Mdiamond Msquare box box3d circle component
diamond doublecircle doubleoctagon egg ellipse folder
hexagon house invhouse invtrapezium invtriangle none
note octagon parallelogram pentagon plaintext point
polygon rect rectangle septagon square tab trapezium
triangle tripleoctagon)
STYLES =
%w(dashed dotted solid invis bold filled diagonals rounded)
ARROW_RE =
/(?:o?[lr]?(?:box|crow|diamond|dot|inv|none|normal|tee|vee)){1,4}/
ARROWS =
%w(box crow diamond dot inv none normal tee vee)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = nil, graph = nil, &block) ⇒ Graph

Creates a new graph object. Optional name and parent graph are available. Also takes an optional block for DSL-like use.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/graph.rb', line 122

def initialize name = nil, graph = nil, &block
  @name = name
  @graph = graph
  graph << self if graph
  @nodes  = Hash.new { |h,k| h[k] = Node.new self, k }
  @edges  = Hash.new { |h,k|
    h[k] = Hash.new { |h2, k2| h2[k2] = Edge.new self, self[k], self[k2] }
  }
  @graph_attribs = []
  @node_attribs  = []
  @edge_attribs  = []
  @subgraphs     = []

  self.scheme = graph.scheme if graph
  node_attribs << scheme if scheme

  instance_eval(&block) if block
end

Instance Attribute Details

#edge_attribsObject (readonly)

Global attributes for edges in this graph.



91
92
93
# File 'lib/graph.rb', line 91

def edge_attribs
  @edge_attribs
end

#edgesObject (readonly)

The hash of hashes of edges in this graph. Use #[] or #node to create edges.



96
97
98
# File 'lib/graph.rb', line 96

def edges
  @edges
end

#graphObject

A parent graph, if any. Only used for subgraphs.



80
81
82
# File 'lib/graph.rb', line 80

def graph
  @graph
end

#graph_attribsObject (readonly)

Global attributes for this graph.



101
102
103
# File 'lib/graph.rb', line 101

def graph_attribs
  @graph_attribs
end

#nameObject

The name of the graph. Optional for graphs and subgraphs. Prefix the name of a subgraph with “cluster” for subgraph that is boxed.



86
87
88
# File 'lib/graph.rb', line 86

def name
  @name
end

#node_attribsObject (readonly)

Global attributes for nodes in this graph.



106
107
108
# File 'lib/graph.rb', line 106

def node_attribs
  @node_attribs
end

#nodesObject (readonly)

The hash of nodes in this graph. Use #[] or #node to create nodes.



111
112
113
# File 'lib/graph.rb', line 111

def nodes
  @nodes
end

#schemeObject

Shortcut method to create a new colorscheme Attribute instance. If passed n, name must match one of the brewer color scheme names and it will generate accessors for each fillcolor as well as push the colorscheme onto the node_attribs.



190
191
192
# File 'lib/graph.rb', line 190

def scheme
  @scheme
end

#subgraphsObject (readonly)

An array of subgraphs.



116
117
118
# File 'lib/graph.rb', line 116

def subgraphs
  @subgraphs
end

Instance Method Details

#<<(subgraph) ⇒ Object

Push a subgraph into the current graph. Sets the subgraph’s graph to self.



144
145
146
147
# File 'lib/graph.rb', line 144

def << subgraph
  subgraphs << subgraph
  subgraph.graph = self
end

#[](name) ⇒ Object

Access a node by name



152
153
154
# File 'lib/graph.rb', line 152

def [] name
  nodes[name]
end

#arrowhead(shape) ⇒ Object

Raises:

  • (ArgumentError)


156
157
158
159
# File 'lib/graph.rb', line 156

def arrowhead shape
  raise ArgumentError, "Bad arrow shape: #{shape}" unless shape =~ ARROW_RE
  Attribute.new "arrowhead = #{shape}"
end

#arrowsize(size) ⇒ Object



166
167
168
# File 'lib/graph.rb', line 166

def arrowsize size
  Attribute.new "arrowsize = #{size}"
end

#arrowtail(shape) ⇒ Object

Raises:

  • (ArgumentError)


161
162
163
164
# File 'lib/graph.rb', line 161

def arrowtail shape
  raise ArgumentError, "Bad arrow shape: #{shape}" unless shape =~ ARROW_RE
  Attribute.new "arrowtail = #{shape}"
end

#boxesObject

A convenience method to set the global node attributes to use boxes.



173
174
175
# File 'lib/graph.rb', line 173

def boxes
  node_attribs << shape("box")
end

#cluster(name, &block) ⇒ Object

Shortcut method to create a clustered subgraph in the current graph. Use with the top-level digraph method in block form for a graph DSL.



327
328
329
# File 'lib/graph.rb', line 327

def cluster name, &block
  subgraph "cluster_#{name}", &block
end

#color(color) ⇒ Object

Shortcut method to create a new color Attribute instance.



180
181
182
# File 'lib/graph.rb', line 180

def color color
  Attribute.new "color = #{color}"
end

#colorscheme(name, n = nil) ⇒ Object



192
193
194
195
196
197
198
199
# File 'lib/graph.rb', line 192

def colorscheme name, n = nil
  self.scheme = Attribute.new "colorscheme = #{name}#{n}"
  max = COLOR_SCHEME_MAX[name.to_sym]

  node_attribs << scheme if max

  scheme
end

#edge(*names) ⇒ Object

Define one or more edges.

edge "a", "b", "c", ...

is equivalent to:

edge "a", "b"
edge "b", "c"
...


218
219
220
221
222
223
224
# File 'lib/graph.rb', line 218

def edge(*names)
  last = nil
  names.each_cons(2) do |from, to|
    last = self[from][to]
  end
  last
end

#fillcolor(n) ⇒ Object

Shortcut method to create a new fillcolor Attribute instance.



242
243
244
# File 'lib/graph.rb', line 242

def fillcolor n
  Attribute.new "fillcolor = #{n}"
end

#font(name) ⇒ Object

Shortcut method to create a new font Attribute instance. You can pass in both the name and an optional font size.



250
251
252
# File 'lib/graph.rb', line 250

def font name
  Attribute.new "fontname = #{name.inspect}"
end

#fontsize(size) ⇒ Object



254
255
256
# File 'lib/graph.rb', line 254

def fontsize size
  Attribute.new "fontsize = #{size}"
end

#invertObject

Creates a new Graph whose edges point the other direction.



229
230
231
232
233
234
235
236
237
# File 'lib/graph.rb', line 229

def invert
  result = self.class.new
  edges.each do |from, h|
    h.each do |to, edge|
      result[to][from]
    end
  end
  result
end

#label(name) ⇒ Object

Shortcut method to set the graph’s label. Usually used with subgraphs.



261
262
263
# File 'lib/graph.rb', line 261

def label name
  graph_attribs << "label = \"#{name.gsub(/\n/, '\n')}\""
end

#node(name, label = nil) ⇒ Object

Access a node by name, supplying an optional label



268
269
270
271
272
# File 'lib/graph.rb', line 268

def node name, label = nil
  n = nodes[name]
  n.label label if label
  n
end

#orient(dir = "TB") ⇒ Object

Shortcut method to specify the orientation of the graph. Defaults to the graphviz default “TB”.



278
279
280
# File 'lib/graph.rb', line 278

def orient dir = "TB"
  graph_attribs << "rankdir = #{dir}"
end

#rotate(dir = "LR") ⇒ Object

Shortcut method to specify the orientation of the graph. Defaults to “LR”.



285
286
287
# File 'lib/graph.rb', line 285

def rotate dir = "LR"
  orient dir
end

#save(path, type = nil) ⇒ Object

Saves out both a dot file to path and an image for the specified type. Specify type as nil to skip exporting an image.



293
294
295
296
297
298
# File 'lib/graph.rb', line 293

def save path, type = nil
  File.open "#{path}.dot", "w" do |f|
    f.puts self.to_s
  end
  system "dot -T#{type} #{path}.dot > #{path}.#{type}" if type
end

#shape(shape) ⇒ Object

Shortcut method to create a new shape Attribute instance.



303
304
305
# File 'lib/graph.rb', line 303

def shape shape
  Attribute.new "shape = #{shape}"
end

#style(name) ⇒ Object

Shortcut method to create a new style Attribute instance.



310
311
312
# File 'lib/graph.rb', line 310

def style name
  Attribute.new "style = #{name}"
end

#subgraph(name = nil, &block) ⇒ Object

Shortcut method to create a subgraph in the current graph. Use with the top-level digraph method in block form for a graph DSL.



318
319
320
# File 'lib/graph.rb', line 318

def subgraph name = nil, &block
  Graph.new name, self, &block
end

#to_sObject

Outputs a graphviz graph.



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/graph.rb', line 334

def to_s
  result = []

  type = graph ? "subgraph" : "digraph"
  result << "#{type} #{name}"
  result << "  {"

  graph_attribs.each do |line|
    result << "    #{line};"
  end

  unless node_attribs.empty? then
    result << "    node [ #{node_attribs.join(", ")} ];"
  end

  unless edge_attribs.empty? then
    result << "    edge [ #{edge_attribs.join(", ")} ];"
  end

  subgraphs.each do |line|
    result << "    #{line};"
  end

  nodes.each do |name, node|
    result << "    #{node};" if graph or node.attributes? or node.orphan?
  end

  edges.each do |from, deps|
    deps.each do |to, edge|
      result << "    #{edge};"
    end
  end

  result << "  }"
  result.join "\n"
end