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



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/graph.rb', line 127

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.



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

def edge_attribs
  @edge_attribs
end

#edgesObject (readonly)

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



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

def edges
  @edges
end

#graphObject

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



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

def graph
  @graph
end

#graph_attribsObject (readonly)

Global attributes for this graph.



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

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.



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

def name
  @name
end

#node_attribsObject (readonly)

Global attributes for nodes in this graph.



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

def node_attribs
  @node_attribs
end

#nodesObject (readonly)

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



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

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.



195
196
197
# File 'lib/graph.rb', line 195

def scheme
  @scheme
end

#subgraphsObject (readonly)

An array of subgraphs.



121
122
123
# File 'lib/graph.rb', line 121

def subgraphs
  @subgraphs
end

Instance Method Details

#<<(subgraph) ⇒ Object

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



149
150
151
152
# File 'lib/graph.rb', line 149

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

#[](name) ⇒ Object

Access a node by name



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

def [] name
  nodes[name]
end

#arrowhead(shape) ⇒ Object

Raises:

  • (ArgumentError)


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

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

#arrowsize(size) ⇒ Object



171
172
173
# File 'lib/graph.rb', line 171

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

#arrowtail(shape) ⇒ Object

Raises:

  • (ArgumentError)


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

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.



178
179
180
# File 'lib/graph.rb', line 178

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.



332
333
334
# File 'lib/graph.rb', line 332

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

#color(color) ⇒ Object

Shortcut method to create a new color Attribute instance.



185
186
187
# File 'lib/graph.rb', line 185

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

#colorscheme(name, n = nil) ⇒ Object



197
198
199
200
201
202
203
204
# File 'lib/graph.rb', line 197

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


223
224
225
226
227
228
229
# File 'lib/graph.rb', line 223

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.



247
248
249
# File 'lib/graph.rb', line 247

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.



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

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

#fontsize(size) ⇒ Object



259
260
261
# File 'lib/graph.rb', line 259

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

#invertObject

Creates a new Graph whose edges point the other direction.



234
235
236
237
238
239
240
241
242
# File 'lib/graph.rb', line 234

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.



266
267
268
# File 'lib/graph.rb', line 266

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



273
274
275
276
277
# File 'lib/graph.rb', line 273

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



283
284
285
# File 'lib/graph.rb', line 283

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



290
291
292
# File 'lib/graph.rb', line 290

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.



298
299
300
301
302
303
# File 'lib/graph.rb', line 298

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.



308
309
310
# File 'lib/graph.rb', line 308

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

#style(name) ⇒ Object

Shortcut method to create a new style Attribute instance.



315
316
317
# File 'lib/graph.rb', line 315

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.



323
324
325
# File 'lib/graph.rb', line 323

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

#to_sObject

Outputs a graphviz graph.



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
370
371
372
373
374
375
# File 'lib/graph.rb', line 339

def to_s
  result = []

  type = graph ? "subgraph " : "digraph "
  type << "\"#{name}\"" if name and !name.empty?
  result << type
  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