Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Commit

Permalink
Merge branch 'master' into 1-1-stable
Browse files Browse the repository at this point in the history
  • Loading branch information
hone committed Dec 14, 2011
2 parents ebdf81b + f5edc0c commit db01251
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 103 deletions.
9 changes: 5 additions & 4 deletions lib/bundler/cli.rb
Expand Up @@ -507,16 +507,17 @@ def version
Viz requires the ruby-graphviz gem (and its dependencies).
The associated gems must also be installed via 'bundle install'.
D
method_option :file, :type => :string, :default => 'gem_graph.png', :aliases => '-f', :banner => "The name to use for the generated png file."
method_option :file, :type => :string, :default => 'gem_graph', :aliases => '-f', :banner => "The name to use for the generated file. see format option"
method_option :version, :type => :boolean, :default => false, :aliases => '-v', :banner => "Set to show each gem version."
method_option :requirements, :type => :boolean, :default => false, :aliases => '-r', :banner => "Set to show the version of each required dependency."
method_option :format, :type => :string, :default => "png", :aliases => '-F', :banner => "This is output format option. Supported format is png, jpg, svg, dot ..."
def viz
output_file = File.expand_path(options[:file])
graph = Graph.new( Bundler.load )
output_format = options[:format]
graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format])

begin
graph.viz(output_file, options[:version], options[:requirements])
Bundler.ui.info output_file
graph.viz
rescue LoadError => e
Bundler.ui.error e.inspect
Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
Expand Down
216 changes: 117 additions & 99 deletions lib/bundler/graph.rb
@@ -1,130 +1,148 @@
require 'set'
module Bundler
class Graph
GRAPH_NAME = :Gemfile

USER_OPTIONS = {:style => 'filled', :fillcolor => '#B9B9D5'}.freeze
def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png")
@env = env
@output_file = output_file
@show_version = show_version
@show_requirements = show_requirements
@output_format = output_format

def initialize(env)
@env = env
end

def nodes
populate
@nodes
end
@groups = []
@relations = Hash.new {|h, k| h[k] = Set.new}
@node_options = {}
@edge_options = {}

def groups
populate
@groups
_populate_relations
end

def viz(output_file, show_gem_versions = false, show_dependency_requirements = false)
require 'graphviz'
populate
attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format

graph_viz = GraphViz::new('Gemfile', {:concentrate => true, :normalize => true, :nodesep => 0.55})
graph_viz.edge[:fontname] = graph_viz.node[:fontname] = 'Arial, Helvetica, SansSerif'
graph_viz.edge[:fontsize] = 12

viz_nodes = {}
def viz
GraphVizClient.new(self).run
end

# 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
private

group_nodes = {}
@groups.each do |name, dependencies|
group_nodes[name] = graph_viz.add_node(name.to_s, { :shape => 'box3d', :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
def _populate_relations
relations = Hash.new {|h, k| h[k] = Set.new}
parent_dependencies = _groups.values.to_set.flatten
while true
if parent_dependencies.empty?
break
else
tmp = Set.new
parent_dependencies.each do |dependency|
child_dependencies = dependency.to_spec.runtime_dependencies.to_set
relations[dependency.name] += child_dependencies.to_set
@relations[dependency.name] += child_dependencies.map(&:name).to_set
tmp += child_dependencies

@node_options[dependency.name] = {:label => _make_label(dependency, :node)}
child_dependencies.each do |c_dependency|
@edge_options["#{dependency.name}_#{c_dependency.name}"] = {:label => _make_label(c_dependency, :edge)}
end
end
graph_viz.add_edge( group_nodes[name], viz_nodes[dependency.name], options )
parent_dependencies = tmp
end
end
@relations
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
def _groups
relations = Hash.new {|h, k| h[k] = Set.new}
@env.current_dependencies.each do |dependency|
dependency.groups.each do |group|
relations[group.to_s].add(dependency)
@relations[group.to_s].add(dependency.name)

graph_viz.add_edge( node, viz_nodes[dependency.name], options )
@node_options[group.to_s] ||= {:label => _make_label(group, :node)}
@edge_options["#{group}_#{dependency.name}"] = {:label => _make_label(dependency, :edge)}
end
end

graph_viz.output( :png => output_file )
@groups = relations.keys
relations
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.current_dependencies.each do |dependency|
next unless 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
def _make_label(symbol_or_string_or_dependency, element_type)
case element_type.to_sym
when :node
if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
label = symbol_or_string_or_dependency.name.dup
label << "\n#{symbol_or_string_or_dependency.to_spec.version.to_s}" if @show_version
else
label = symbol_or_string_or_dependency.to_s
end
when :edge
label = nil
if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
label = tmp if tmp != ">= 0"
end
else
raise ArgumentError, "2nd argument is invalid"
end
label
end

# walk though a final time and add edges
@env.specs.each do |spec|
class GraphVizClient
def initialize(graph_instance)
@graph_name = graph_instance.class::GRAPH_NAME
@groups = graph_instance.groups
@relations = graph_instance.relations
@node_options = graph_instance.node_options
@edge_options = graph_instance.edge_options
@output_file = graph_instance.output_file
@output_format = graph_instance.output_format
end

from = @nodes[spec.name]
spec.runtime_dependencies.each do |dependency|
from.dependencies << dependency
def g
require 'graphviz'
@g ||= ::GraphViz.digraph(@graph_name, {:concentrate => true, :normalize => true, :nodesep => 0.55}) do |g|
g.edge[:weight] = 2
g.edge[:fontname] = g.node[:fontname] = 'Arial, Helvetica, SansSerif'
g.edge[:fontsize] = 12
end

end

@nodes.freeze
@groups.freeze
@populated = true
end

end
def run
@groups.each do |group|
g.add_node(
group,
{:style => 'filled',
:fillcolor => '#B9B9D5',
:shape => "box3d",
:fontsize => 16}.merge(@node_options[group])
)
end

# Add version info
class GraphNode
@relations.each do |parent, children|
children.each do |child|
if @groups.include?(parent)
g.add_node(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
g.add_edge(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
else
g.add_node(child, @node_options[child])
g.add_edge(parent, child, @edge_options["#{parent}_#{child}"])
end
end
end

def initialize(name, version)
@name = name
@version = version
@is_user = false
@dependencies = []
if @output_format.to_s == "debug"
$stdout.puts g.output :none => String
Bundler.ui.info "debugging bundle viz..."
else
begin
g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
Bundler.ui.info "#{@output_file}.#{@output_format}"
rescue ArgumentError => e
$stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
raise e
end
end
end
end

attr_reader :name, :dependencies, :version
attr_accessor :is_user

end
end

0 comments on commit db01251

Please sign in to comment.