bundle-new/lib/bundler/graph.rb

167 lines
5.5 KiB
Ruby
Raw Permalink Normal View History

2019-10-06 15:45:34 +00:00
require 'set'
module Bundler
class Graph
GRAPH_NAME = :Gemfile
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
@groups = []
@relations = Hash.new {|h, k| h[k] = Set.new}
@node_options = {}
@edge_options = {}
_patching_gem_dependency_class
_populate_relations
end
attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
def viz
GraphVizClient.new(self).run
end
private
def _populate_relations
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.map(&:name).to_set
tmp += child_dependencies
@node_options[dependency.name] = _make_label(dependency, :node)
child_dependencies.each do |c_dependency|
@edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
end
end
parent_dependencies = tmp
end
end
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)
@node_options[group.to_s] ||= _make_label(group, :node)
@edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
end
end
@groups = relations.keys
relations
end
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.nil? ? {} : { :label => label }
end
def _patching_gem_dependency_class
# method borrow from rubygems/dependency.rb
# redefinition of matching_specs will also redefine to_spec and to_specs
Gem::Dependency.class_eval do
def matching_specs platform_only = false
matches = Bundler.load.specs.select { |spec|
self.name == spec.name and
requirement.satisfied_by? spec.version
}
if platform_only
matches.reject! { |spec|
not Gem::Platform.match spec.platform
}
end
matches = matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed
end
end
end
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
def g
@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
def run
@groups.each do |group|
g.add_nodes(
group,
{:style => 'filled',
:fillcolor => '#B9B9D5',
:shape => "box3d",
:fontsize => 16}.merge(@node_options[group])
)
end
@relations.each do |parent, children|
children.each do |child|
if @groups.include?(parent)
g.add_nodes(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
g.add_edges(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
else
g.add_nodes(child, @node_options[child])
g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
end
end
end
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
end
end