bundler-graph 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 11e639242f81d41c2083dbeb1a82ecdbaff2f9363468edb88e4149fc7656412d
4
+ data.tar.gz: 605a7151c65abc1d9939aded3341fcedc9d900a47d828f9c932102223da516e9
5
+ SHA512:
6
+ metadata.gz: b38c2a4c86aef63bb3d5abb3e71befcf0e814076a96839649a3accc02ee1ae33a365aab8a642456aab9a964edfa3ad9bda4c098af6f8e841153eacd005e7100f
7
+ data.tar.gz: 87b7c46d1a73ad2f0ee372c7a4efd4c4c346119c9cb26eafc3697667eaee3cd6be4ec0af0be515b83709c201c92074f3e086778aa51643e087cb94cef28b45a1
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in bundler-viz.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Hiroshi SHIBATA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # Bundler Graph plugin
2
+
3
+ `graph` generates a PNG file of the current `Gemfile` as a dependency graph.
4
+ `graph` requires the ruby-graphviz gem (and its dependencies).
5
+
6
+ The associated gems must also be installed via `bundle install`.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ plugin 'bundler-graph'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle install
19
+
20
+ ## Usage
21
+
22
+ ```
23
+ bundle graph [--file=FILE]
24
+ [--format=FORMAT]
25
+ [--requirements]
26
+ [--version]
27
+ [--without=GROUP GROUP]
28
+ ```
29
+
30
+ ## Development
31
+
32
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
33
+
34
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
35
+
36
+ ## Contributing
37
+
38
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rubygems/bundler-graph.
39
+
40
+ ## License
41
+
42
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "bundler/viz"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/bundler/graph/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "bundler-graph"
7
+ spec.version = Bundler::Graph::VERSION
8
+ spec.authors = ["Hiroshi SHIBATA"]
9
+ spec.email = ["hsbt@ruby-lang.org"]
10
+
11
+ spec.summary = "Generates a visual dependency graph for your Gemfile"
12
+ spec.description = "`graph` generates a PNG file of the current `Gemfile(5)` as a dependency graph."
13
+ spec.homepage = "https://github.com/rubygems/bundler-graph"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.4.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+
20
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
+ `git ls-files -z`.split("\x0").reject do |f|
22
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
23
+ end
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ class Graph
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ module Bundler
5
+ class Graph
6
+ GRAPH_NAME = :Gemfile
7
+
8
+ def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png", without = [])
9
+ @env = env
10
+ @output_file = output_file
11
+ @show_version = show_version
12
+ @show_requirements = show_requirements
13
+ @output_format = output_format
14
+ @without_groups = without.map(&:to_sym)
15
+
16
+ @groups = []
17
+ @relations = Hash.new {|h, k| h[k] = Set.new }
18
+ @node_options = {}
19
+ @edge_options = {}
20
+
21
+ _populate_relations
22
+ end
23
+
24
+ attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
25
+
26
+ def viz
27
+ GraphVizClient.new(self).run
28
+ end
29
+
30
+ private
31
+
32
+ def _populate_relations
33
+ parent_dependencies = _groups.values.to_set.flatten
34
+ loop do
35
+ break if parent_dependencies.empty?
36
+
37
+ tmp = Set.new
38
+ parent_dependencies.each do |dependency|
39
+ child_dependencies = spec_for_dependency(dependency).runtime_dependencies.to_set
40
+ @relations[dependency.name] += child_dependencies.map(&:name).to_set
41
+ tmp += child_dependencies
42
+
43
+ @node_options[dependency.name] = _make_label(dependency, :node)
44
+ child_dependencies.each do |c_dependency|
45
+ @edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
46
+ end
47
+ end
48
+ parent_dependencies = tmp
49
+ end
50
+ end
51
+
52
+ def _groups
53
+ relations = Hash.new {|h, k| h[k] = Set.new }
54
+ @env.current_dependencies.each do |dependency|
55
+ dependency.groups.each do |group|
56
+ next if @without_groups.include?(group)
57
+
58
+ relations[group.to_s].add(dependency)
59
+ @relations[group.to_s].add(dependency.name)
60
+
61
+ @node_options[group.to_s] ||= _make_label(group, :node)
62
+ @edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
63
+ end
64
+ end
65
+ @groups = relations.keys
66
+ relations
67
+ end
68
+
69
+ def _make_label(symbol_or_string_or_dependency, element_type)
70
+ case element_type.to_sym
71
+ when :node
72
+ if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
73
+ label = symbol_or_string_or_dependency.name.dup
74
+ label << "\n#{spec_for_dependency(symbol_or_string_or_dependency).version}" if @show_version
75
+ else
76
+ label = symbol_or_string_or_dependency.to_s
77
+ end
78
+ when :edge
79
+ label = nil
80
+ if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
81
+ tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
82
+ label = tmp if tmp != ">= 0"
83
+ end
84
+ else
85
+ raise ArgumentError, "2nd argument is invalid"
86
+ end
87
+ label.nil? ? {} : { :label => label }
88
+ end
89
+
90
+ def spec_for_dependency(dependency)
91
+ @env.requested_specs.find {|s| s.name == dependency.name }
92
+ end
93
+
94
+ class GraphVizClient
95
+ def initialize(graph_instance)
96
+ @graph_name = graph_instance.class::GRAPH_NAME
97
+ @groups = graph_instance.groups
98
+ @relations = graph_instance.relations
99
+ @node_options = graph_instance.node_options
100
+ @edge_options = graph_instance.edge_options
101
+ @output_file = graph_instance.output_file
102
+ @output_format = graph_instance.output_format
103
+ end
104
+
105
+ def g
106
+ @g ||= ::GraphViz.digraph(@graph_name, :concentrate => true, :normalize => true, :nodesep => 0.55) do |g|
107
+ g.edge[:weight] = 2
108
+ g.edge[:fontname] = g.node[:fontname] = "Arial, Helvetica, SansSerif"
109
+ g.edge[:fontsize] = 12
110
+ end
111
+ end
112
+
113
+ def run
114
+ @groups.each do |group|
115
+ g.add_nodes(
116
+ group, {
117
+ :style => "filled",
118
+ :fillcolor => "#B9B9D5",
119
+ :shape => "box3d",
120
+ :fontsize => 16,
121
+ }.merge(@node_options[group])
122
+ )
123
+ end
124
+
125
+ @relations.each do |parent, children|
126
+ children.each do |child|
127
+ if @groups.include?(parent)
128
+ g.add_nodes(child, { :style => "filled", :fillcolor => "#B9B9D5" }.merge(@node_options[child]))
129
+ g.add_edges(parent, child, { :constraint => false }.merge(@edge_options["#{parent}_#{child}"]))
130
+ else
131
+ g.add_nodes(child, @node_options[child])
132
+ g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
133
+ end
134
+ end
135
+ end
136
+
137
+ if @output_format.to_s == "debug"
138
+ $stdout.puts g.output :none => String
139
+ Bundler.ui.info "debugging bundle viz..."
140
+ else
141
+ begin
142
+ g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
143
+ Bundler.ui.info "#{@output_file}.#{@output_format}"
144
+ rescue ArgumentError => e
145
+ warn "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
146
+ raise e
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/plugin/api"
4
+ require "optparse"
5
+ require_relative "graph"
6
+
7
+ module Bundler
8
+ class GraphCLI < Plugin::API
9
+
10
+ command "graph"
11
+
12
+ def exec(command, argv)
13
+ # make sure we get the right `graphviz`. There is also a `graphviz`
14
+ # gem we're not built to support
15
+ gem "ruby-graphviz"
16
+ require "graphviz"
17
+
18
+ options = {
19
+ file: "gem_graph",
20
+ format: "png",
21
+ requirements: false,
22
+ version: false,
23
+ without: []
24
+ }
25
+ opt = OptionParser.new
26
+ opt.on('-f', '--file', 'The name to use for the generated file. See `--format` option') {|v| options[:file] = v }
27
+ opt.on('-F', '--format', 'This is output format option. Supported format is png, jpg, svg, dot ...') {|v| options[:format] = v }
28
+ opt.on('-R', '--requirements', 'Set to show the version of each required dependency.') {|v| options[:requirements] = true }
29
+ opt.on('-v', '--version', 'Set to show each gem version.') {|v| options[:version] = true }
30
+ opt.on('-W', '--without', 'Exclude gems that are part of the specified named group.') {|v| options[:without] = v }
31
+ opt.parse!(argv)
32
+
33
+ options[:without] = options[:without].join(":").tr(" ", ":").split(":")
34
+ output_file = File.expand_path(options[:file])
35
+
36
+ graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format], options[:without])
37
+ graph.viz
38
+ rescue LoadError => e
39
+ Bundler.ui.error e.inspect
40
+ Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
41
+ Bundler.ui.warn "`gem install ruby-graphviz`"
42
+ rescue StandardError => e
43
+ raise unless e.message =~ /GraphViz not installed or dot not in PATH/
44
+ Bundler.ui.error e.message
45
+ Bundler.ui.warn "Please install GraphViz. On a Mac with Homebrew, you can run `brew install graphviz`."
46
+ end
47
+ end
48
+ end
data/plugins.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/bundler/graph_cli"
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bundler-graph
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hiroshi SHIBATA
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-11-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: "`graph` generates a PNG file of the current `Gemfile(5)` as a dependency
14
+ graph."
15
+ email:
16
+ - hsbt@ruby-lang.org
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - Gemfile
22
+ - LICENSE.txt
23
+ - README.md
24
+ - Rakefile
25
+ - bin/console
26
+ - bin/setup
27
+ - bundler-graph.gemspec
28
+ - lib/bundler/graph.rb
29
+ - lib/bundler/graph/version.rb
30
+ - lib/bundler/graph_cli.rb
31
+ - plugins.rb
32
+ homepage: https://github.com/rubygems/bundler-graph
33
+ licenses:
34
+ - MIT
35
+ metadata:
36
+ homepage_uri: https://github.com/rubygems/bundler-graph
37
+ source_code_uri: https://github.com/rubygems/bundler-graph
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.4.0
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubygems_version: 3.2.26
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: Generates a visual dependency graph for your Gemfile
57
+ test_files: []