1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
module Bundler
class Graph
USER_OPTIONS = {:style => 'filled, bold', :fillcolor => '#cccccc'}.freeze
def initialize(env)
@env = env
end
def nodes
populate
@nodes
end
def groups
populate
@groups
end
def viz(output_file, show_gem_versions = false, show_dependency_requirements = false)
require 'graphviz'
populate
graph_viz = GraphViz::new('Gemfile', {:concentrate => true, :dpi => 65 } )
graph_viz.edge[:fontname] = graph_viz.node[:fontname] = 'Arial, Helvetica, SansSerif'
graph_viz.edge[:fontsize] = 12
viz_nodes = {}
# populate all of the nodes
nodes.each do |name, node|
label = name.dup
label << "\n#{node.version}" if show_gem_versions
options = { :label => label }
options.merge!( USER_OPTIONS ) if node.is_user
viz_nodes[name] = graph_viz.add_node( name, options )
end
group_nodes = {}
@groups.each do |name, dependencies|
group_nodes[name] = graph_viz.add_node(name.to_s, { :shape => 'folder', :fontsize => 16 }.merge(USER_OPTIONS))
dependencies.each do |dependency|
options = { :weight => 2 }
if show_dependency_requirements && (dependency.requirement.to_s != ">= 0")
options[:label] = dependency.requirement.to_s
end
graph_viz.add_edge( group_nodes[name], viz_nodes[dependency.name], options )
end
end
@groups.keys.select { |group| group != :default }.each do |group|
graph_viz.add_edge( group_nodes[group], group_nodes[:default], { :weight => 2 } )
end
viz_nodes.each do |name, node|
nodes[name].dependencies.each do |dependency|
options = { }
if nodes[dependency.name].is_user
options[:constraint] = false
end
if show_dependency_requirements && (dependency.requirement.to_s != ">= 0")
options[:label] = dependency.requirement.to_s
end
graph_viz.add_edge( node, viz_nodes[dependency.name], options )
end
end
graph_viz.output( :png => output_file )
end
private
def populate
return if @populated
# hash of name => GraphNode
@nodes = {}
@groups = {}
# Populate @nodes
@env.specs.each { |spec| @nodes[spec.name] = GraphNode.new(spec.name, spec.version) }
# For gems in Gemfile, add details
@env.dependencies.each do |dependency|
node = @nodes[dependency.name]
node.is_user = true
dependency.groups.each do |group|
if @groups.has_key? group
group = @groups[group]
else
group = @groups[group] = []
end
group << dependency
end
end
# walk though a final time and add edges
@env.specs.each do |spec|
from = @nodes[spec.name]
spec.runtime_dependencies.each do |dependency|
from.dependencies << dependency
end
end
@nodes.freeze
@groups.freeze
@populated = true
end
end
# Add version info
class GraphNode
def initialize(name, version)
@name = name
@version = version
@is_user = false
@dependencies = []
end
attr_reader :name, :dependencies, :version
attr_accessor :is_user
end
end
|