summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTerence Lee <hone02@gmail.com>2011-12-14 12:54:01 -0500
committerTerence Lee <hone02@gmail.com>2011-12-14 12:54:01 -0500
commitdb0125185328b498e9833f6926b0c0a95f3d8aa2 (patch)
tree267959c9e08b686fa0417d63715e68d82c1b303f
parentebdf81b6cad25d7e86fd561a62f6b1f54ab246c6 (diff)
parentf5edc0ce84b7c96734db7e2ac37eb348086c5040 (diff)
downloadbundler-db0125185328b498e9833f6926b0c0a95f3d8aa2.tar.gz
Merge branch 'master' into 1-1-stable
-rw-r--r--lib/bundler/cli.rb9
-rw-r--r--lib/bundler/graph.rb216
2 files changed, 122 insertions, 103 deletions
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 5a86ec572a..9e3a3344f2 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -507,16 +507,17 @@ module Bundler
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:"
diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb
index 3cea736ac2..6ab23d90ef 100644
--- a/lib/bundler/graph.rb
+++ b/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