bundler-stats 1.3.4 → 2.0.1
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 +5 -5
- data/.rspec +1 -0
- data/Guardfile +0 -12
- data/README.md +95 -62
- data/bin/bundler-stats +1 -1
- data/bundler-stats.gemspec +2 -0
- data/lib/bundler/stats.rb +4 -3
- data/lib/bundler/stats/cli.rb +37 -32
- data/lib/bundler/stats/printer.rb +143 -0
- data/lib/bundler/stats/version.rb +1 -1
- data/spec/lib/bundler/stats/printer_spec.rb +214 -0
- metadata +34 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: effbf5fc1dcb265737923b548467df2b831f444638f673e37622a2efd564cb54
|
|
4
|
+
data.tar.gz: 23cae592f904d2cdaef97b5597f953b0a927f24368d5f6e1b8763224391d4884
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: de70cf8fedea1260329b24fea38e1f564215ae7f6bf67d5d0d87d6aa1b639cad39ced3625069ea6ecb38e315a3fec7de561cde0a331e951d550234f90e6ed0c7
|
|
7
|
+
data.tar.gz: 606dc092473b823593dc2baec837c91956a6554c31f66990afc1f89052dac95f9c0209324b7e643429e04fe922025bcb090264f9730a7b6d900706bf6138ccfd
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/Guardfile
CHANGED
|
@@ -15,18 +15,6 @@
|
|
|
15
15
|
#
|
|
16
16
|
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
|
17
17
|
|
|
18
|
-
guard :bundler do
|
|
19
|
-
require 'guard/bundler'
|
|
20
|
-
require 'guard/bundler/verify'
|
|
21
|
-
helper = Guard::Bundler::Verify.new
|
|
22
|
-
|
|
23
|
-
files = ['Gemfile']
|
|
24
|
-
files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
|
|
25
|
-
|
|
26
|
-
# Assume files are symlinked from somewhere
|
|
27
|
-
files.each { |file| watch(helper.real_path(file)) }
|
|
28
|
-
end
|
|
29
|
-
|
|
30
18
|
guard :rspec, cmd: 'rspec' do
|
|
31
19
|
watch(%r{^spec/.+_spec\.rb$})
|
|
32
20
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
data/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
Bundler Stats
|
|
2
2
|
=============
|
|
3
3
|
|
|
4
|
-
You remember that time [someone yanked their
|
|
5
|
-
and
|
|
4
|
+
You remember that time [someone yanked their
|
|
5
|
+
library](http://blog.npmjs.org/post/141577284765/kik-left-pad-and-npm) and the
|
|
6
|
+
entire Node universe fell apart? Yeah, me too. And all the
|
|
6
7
|
[thinkpieces](http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/)
|
|
7
|
-
that came out just afterward were right: you should be careful about
|
|
8
|
-
|
|
8
|
+
that came out just afterward were right: you should be careful about what
|
|
9
|
+
dependencies you include in your project.
|
|
9
10
|
|
|
10
11
|
This project gives you some tools you can use with an existing Gemfile to
|
|
11
|
-
determine which gems are including long trees of their own dependencies,
|
|
12
|
-
|
|
12
|
+
determine which gems are including long trees of their own dependencies, and
|
|
13
|
+
which you can potentially remove.
|
|
13
14
|
|
|
14
15
|
This is an exploratory tool, and I'd be interested to hear what other criteria
|
|
15
16
|
would be useful in determining what tools to remove.
|
|
@@ -33,75 +34,106 @@ Usage
|
|
|
33
34
|
bundle-stats version # Prints the bundler-stats version
|
|
34
35
|
bundle-stats versions TARGET # Shows versions requirements for target in other dependencies
|
|
35
36
|
|
|
36
|
-
The most obvious thing to do is run the command by itself, which should help
|
|
37
|
+
The most obvious thing to do is run the command by itself, which should help
|
|
38
|
+
identify problem areas:
|
|
37
39
|
|
|
38
40
|
> bundle-stats
|
|
39
41
|
|
|
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
|
-
|
|
42
|
+
+----------------------------|------------|----------------+
|
|
43
|
+
| Name | Total Deps | 1st Level Deps |
|
|
44
|
+
+----------------------------|------------|----------------+
|
|
45
|
+
| rails_admin | 60 | 12 |
|
|
46
|
+
| rails | 40 | 12 |
|
|
47
|
+
| compass-rails | 35 | 3 |
|
|
48
|
+
| haml-rails | 29 | 5 |
|
|
49
|
+
| rspec-rails | 27 | 7 |
|
|
50
|
+
| sass-rails | 26 | 5 |
|
|
51
|
+
| devise | 26 | 5 |
|
|
52
|
+
| scenic | 25 | 2 |
|
|
53
|
+
| coffee-rails | 25 | 2 |
|
|
54
|
+
| guard-rubocop | 24 | 2 |
|
|
55
|
+
| versionist | 23 | 3 |
|
|
56
|
+
| factory_bot_rails | 23 | 2 |
|
|
57
|
+
| ... omitted stuff here ... |
|
|
58
|
+
+----------------------------|------------|----------------+
|
|
59
|
+
|
|
60
|
+
Declared Gems 56
|
|
61
|
+
Total Gems 170
|
|
62
|
+
Unpinned Versions 54
|
|
63
|
+
Github Refs 0
|
|
64
|
+
|
|
65
|
+
It looks like rails_admin is a huge problem. Use `show` to investigate:
|
|
66
|
+
|
|
67
|
+
> bundle-stats show rails_admin
|
|
68
|
+
bundle-stats for rails_admin
|
|
69
|
+
|
|
70
|
+
+--------------------------------|----------------------------------------+
|
|
71
|
+
| Depended Upon By (0) | |
|
|
72
|
+
| Depends On (60) | builder, coffee-rails |
|
|
73
|
+
| | font-awesome-rails, haml, jquery-rails |
|
|
74
|
+
| | jquery-ui-rails, kaminari, nested_form |
|
|
75
|
+
| | rack-pjax, rails, remotipart |
|
|
76
|
+
| | sass-rails, coffee-script, railties |
|
|
77
|
+
| | coffee-script-source, execjs |
|
|
78
|
+
| | actionpack, activesupport |
|
|
79
|
+
| | method_source, rake, thor, actionview |
|
|
80
|
+
| | rack, rack-test, rails-dom-testing |
|
|
81
|
+
| | rails-html-sanitizer, erubi |
|
|
82
|
+
| | concurrent-ruby, i18n, minitest |
|
|
83
|
+
| | tzinfo, thread_safe, nokogiri |
|
|
84
|
+
| | mini_portile2, loofah, crass, temple |
|
|
85
|
+
| | tilt, kaminari-actionview |
|
|
86
|
+
| | kaminari-activerecord, kaminari-core |
|
|
87
|
+
| | activerecord, activemodel, arel |
|
|
88
|
+
| | actioncable, actionmailer, activejob |
|
|
89
|
+
| | activestorage, bundler |
|
|
90
|
+
| | sprockets-rails, nio4r |
|
|
91
|
+
| | websocket-driver, websocket-extensions |
|
|
92
|
+
| | mail, globalid, mini_mime, marcel |
|
|
93
|
+
| | mimemagic, sprockets, sass |
|
|
94
|
+
| Unique to This (9) | font-awesome-rails, kaminari |
|
|
95
|
+
| | nested_form, rack-pjax, remotipart |
|
|
96
|
+
| | kaminari-actionview |
|
|
97
|
+
| | kaminari-activerecord, kaminari-core |
|
|
98
|
+
| | bundler |
|
|
99
|
+
+--------------------------------|----------------------------------------+
|
|
100
|
+
|
|
101
|
+
Removing the dep will only actually remove 9 gems. The rest are shared
|
|
102
|
+
dependencies with other gems like rails. We can also omit trees we aren't going
|
|
103
|
+
to remove (hi rails) by not following them:
|
|
75
104
|
|
|
76
105
|
> bundle-stats show sass-rails --nofollow="railties,activesupport,actionpack"
|
|
77
106
|
bundle-stats for sass-rails
|
|
78
107
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
108
|
+
+--------------------------------|----------------------------------------+
|
|
109
|
+
| Depended Upon By (2) | compass-rails, rails_admin |
|
|
110
|
+
| Depends On (9) | railties, sass, sprockets |
|
|
111
|
+
| | sprockets-rails, tilt, concurrent-ruby |
|
|
112
|
+
| | rack, actionpack, activesupport |
|
|
113
|
+
| Unique to This (0) | |
|
|
114
|
+
+--------------------------------|----------------------------------------+
|
|
82
115
|
|
|
83
116
|
To consume information with a build job or somesuch, all commands can emit JSON:
|
|
84
117
|
|
|
85
118
|
> bundle-stats show sass-rails --nofollow="railties,activesupport,actionpack" -f json
|
|
86
119
|
{
|
|
87
120
|
"name": "sass-rails",
|
|
88
|
-
"total_dependencies":
|
|
89
|
-
"first_level_dependencies":
|
|
121
|
+
"total_dependencies": 9,
|
|
122
|
+
"first_level_dependencies": 5,
|
|
90
123
|
"top_level_dependencies": {
|
|
91
|
-
"
|
|
92
|
-
"
|
|
124
|
+
"compass-rails": "compass-rails (3.1.0)",
|
|
125
|
+
"rails_admin": "rails_admin (1.3.0)"
|
|
93
126
|
},
|
|
94
127
|
"transitive_dependencies": [
|
|
95
|
-
"railties (<
|
|
96
|
-
"sass (~> 3.
|
|
97
|
-
"sprockets (
|
|
98
|
-
"sprockets-rails (
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"rack (
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"activesupport (>= 3.0)"
|
|
128
|
+
"railties (< 6, >= 4.0.0)",
|
|
129
|
+
"sass (~> 3.1)",
|
|
130
|
+
"sprockets (< 4.0, >= 2.8)",
|
|
131
|
+
"sprockets-rails (< 4.0, >= 2.0)",
|
|
132
|
+
"tilt (< 3, >= 1.1)",
|
|
133
|
+
"concurrent-ruby (~> 1.0)",
|
|
134
|
+
"rack (< 3, > 1)",
|
|
135
|
+
"actionpack (>= 4.0)",
|
|
136
|
+
"activesupport (>= 4.0)"
|
|
105
137
|
],
|
|
106
138
|
"potential_removals": [
|
|
107
139
|
|
|
@@ -119,12 +151,13 @@ Contribution is expected to conform to the [Contributor Covenant](https://github
|
|
|
119
151
|
Credits
|
|
120
152
|
-------
|
|
121
153
|
|
|
122
|
-
Thanks to the many kind people at [RailsCamp East
|
|
123
|
-
for the help, the ideas, and the support.
|
|
154
|
+
Thanks to the many kind people at [RailsCamp East
|
|
155
|
+
2016](http://east.railscamp.com) for the help, the ideas, and the support.
|
|
124
156
|
|
|
125
157
|
Thanks to Isaac Bowen for being pedantic about speeling.
|
|
126
158
|
|
|
127
159
|
License
|
|
128
160
|
-------
|
|
129
161
|
|
|
130
|
-
This software is released under the [MIT
|
|
162
|
+
This software is released under the [MIT
|
|
163
|
+
License](https://github.com/jmmastey/bundler-stats/blob/master/MIT-LICENSE).
|
data/bin/bundler-stats
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
bundle-stats
|
|
1
|
+
bin/bundle-stats
|
data/bundler-stats.gemspec
CHANGED
|
@@ -40,5 +40,7 @@ Gem::Specification.new do |gem|
|
|
|
40
40
|
|
|
41
41
|
gem.add_development_dependency "rspec", "~> 3.4"
|
|
42
42
|
gem.add_development_dependency "guard", "~> 2.13"
|
|
43
|
+
gem.add_development_dependency "guard-rspec"
|
|
43
44
|
gem.add_development_dependency "pry", "~> 0.10"
|
|
45
|
+
gem.add_development_dependency "rb-readline"
|
|
44
46
|
end
|
data/lib/bundler/stats.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
require_relative 'stats/version'
|
|
2
|
-
require_relative 'stats/tree'
|
|
3
|
-
require_relative 'stats/remover'
|
|
4
1
|
require_relative 'stats/calculator'
|
|
2
|
+
require_relative 'stats/printer'
|
|
3
|
+
require_relative 'stats/remover'
|
|
4
|
+
require_relative 'stats/tree'
|
|
5
|
+
require_relative 'stats/version'
|
data/lib/bundler/stats/cli.rb
CHANGED
|
@@ -59,56 +59,61 @@ module Bundler
|
|
|
59
59
|
|
|
60
60
|
private
|
|
61
61
|
|
|
62
|
-
# TODO: just install table_print, 'eh?
|
|
63
62
|
def draw_stats(gem_stats, summary)
|
|
64
63
|
max_name_length = gem_stats.map { |gem| gem[:name].length }.max
|
|
65
64
|
|
|
66
|
-
say
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
say Printer.new(
|
|
66
|
+
headers: ["Name", "Total Deps", "1st Level Deps"],
|
|
67
|
+
data: gem_stats.map { |stat_line|
|
|
68
|
+
[stat_line[:name], stat_line[:total_dependencies], stat_line[:first_level_dependencies]]
|
|
69
|
+
}).to_s
|
|
70
|
+
|
|
71
|
+
say Printer.new(
|
|
72
|
+
headers: nil,
|
|
73
|
+
borders: false,
|
|
74
|
+
data: [
|
|
75
|
+
["Declared Gems", summary[:declared]],
|
|
76
|
+
["Total Gems", summary[:total]],
|
|
77
|
+
["", ""],
|
|
78
|
+
["Unpinned Versions", summary[:unpinned]],
|
|
79
|
+
["Github Refs", summary[:github]],
|
|
80
|
+
]).to_s
|
|
77
81
|
say ""
|
|
78
|
-
say "Unpinned Versions: #{summary[:unpinned]}"
|
|
79
|
-
say "Github Refs: #{summary[:github]}"
|
|
80
82
|
end
|
|
81
83
|
|
|
82
84
|
def draw_show(stats, target)
|
|
83
85
|
say "bundle-stats for #{target}"
|
|
84
86
|
say ""
|
|
85
|
-
|
|
86
|
-
say
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
|
|
88
|
+
say Printer.new(
|
|
89
|
+
data: [
|
|
90
|
+
["Depended Upon By (#{stats[:top_level_dependencies].count})", stats[:top_level_dependencies].values.map(&:name)],
|
|
91
|
+
["Depends On (#{stats[:transitive_dependencies].count})", stats[:transitive_dependencies].map(&:name)],
|
|
92
|
+
["Unique to This (#{stats[:potential_removals].count})", stats[:potential_removals].map(&:name)],
|
|
93
|
+
]).to_s
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
def draw_versions(stats, target)
|
|
92
97
|
dependers = stats[:top_level_dependencies] # they do the depending
|
|
93
98
|
say "bundle-stats for #{target}"
|
|
94
|
-
say
|
|
95
|
-
|
|
99
|
+
say Printer.new(
|
|
100
|
+
headers: nil,
|
|
101
|
+
borders: false,
|
|
102
|
+
data: [
|
|
103
|
+
["Depended Upon By", stats[:top_level_dependencies].count],
|
|
104
|
+
["Resolved Version", stats[:resolved_version]],
|
|
105
|
+
]).to_s
|
|
96
106
|
|
|
97
|
-
if stats[:resolved_version]
|
|
98
|
-
say "Resolved version is #{stats[:resolved_version]}"
|
|
99
|
-
end
|
|
100
107
|
if dependers.count > 0
|
|
101
|
-
max_name_length = dependers.map { |gem| gem[:name].length }.max
|
|
102
|
-
|
|
103
|
-
say "+-#{"-" * max_name_length}-|-------------------+"
|
|
104
|
-
say "| %-#{max_name_length}s | Required Version |" % ["Name"]
|
|
105
|
-
say "+-#{"-" * max_name_length}-|-------------------+"
|
|
106
|
-
dependers.each do |stat_line|
|
|
107
|
-
say "| %-#{max_name_length}s | %-17s |" % [stat_line[:name], stat_line[:version]]
|
|
108
|
-
end
|
|
109
|
-
say "+-#{"-" * max_name_length}-|-------------------+"
|
|
110
108
|
say ""
|
|
109
|
+
say Printer.new(
|
|
110
|
+
headers: ["Name", "Required Version"],
|
|
111
|
+
data: dependers.map { |stat_line|
|
|
112
|
+
[stat_line[:name], stat_line[:version]]
|
|
113
|
+
}).to_s
|
|
111
114
|
end
|
|
115
|
+
|
|
116
|
+
say ""
|
|
112
117
|
end
|
|
113
118
|
|
|
114
119
|
def build_calculator(options)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# this is somewhat duplicative of the table_print gem, but tbh I like that we
|
|
2
|
+
# don't have many dependencies yet, so I'd rather keep it this way.
|
|
3
|
+
class Bundler::Stats::Printer
|
|
4
|
+
attr_accessor :headers, :data, :borders
|
|
5
|
+
|
|
6
|
+
MIN_COL_SIZE = 10
|
|
7
|
+
|
|
8
|
+
BORDERS = {
|
|
9
|
+
on: { corner: "+", horizontal: "-", vertical: "|" },
|
|
10
|
+
off: { corner: " ", horizontal: " ", vertical: " " },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def initialize(headers: nil, data: [], borders: true)
|
|
14
|
+
@headers = headers
|
|
15
|
+
@data = data
|
|
16
|
+
@borders = borders ? BORDERS[:on] : BORDERS[:off]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
table_data = ([headers] + data).compact
|
|
21
|
+
col_widths = column_widths(table_data)
|
|
22
|
+
|
|
23
|
+
lines = []
|
|
24
|
+
lines << separator_row(col_widths)
|
|
25
|
+
|
|
26
|
+
if headers
|
|
27
|
+
lines << aligned_row(headers, col_widths)
|
|
28
|
+
lines << separator_row(col_widths)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
data.each do |row|
|
|
32
|
+
lines += split_rows(row, col_widths)
|
|
33
|
+
end
|
|
34
|
+
lines << separator_row(col_widths)
|
|
35
|
+
|
|
36
|
+
lines.join("\n")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def terminal_width
|
|
40
|
+
Integer(Kernel.send(:"`", "tput cols"))
|
|
41
|
+
rescue Errno::ENOENT, TypeError, Errno::ENOENT
|
|
42
|
+
80
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def column_widths(table_data)
|
|
46
|
+
num_cols = table_data.first.length
|
|
47
|
+
chrome = 2 + 2 + (num_cols - 1) * 3
|
|
48
|
+
|
|
49
|
+
# doesn't fit at all
|
|
50
|
+
if chrome + (num_cols * MIN_COL_SIZE) > terminal_width
|
|
51
|
+
raise ArgumentError, "Table smooshed. Refusing to print table."
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
data_widths = 0.upto(num_cols - 1).map do |idx|
|
|
55
|
+
max_width(table_data.map { |row| row[idx] })
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# fits comfortably
|
|
59
|
+
if data_widths.inject(&:+) + chrome < terminal_width
|
|
60
|
+
return data_widths
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
free_space = terminal_width
|
|
64
|
+
free_space -= chrome
|
|
65
|
+
free_space -= MIN_COL_SIZE * num_cols
|
|
66
|
+
|
|
67
|
+
# fit uncomfortably
|
|
68
|
+
widths = [MIN_COL_SIZE] * num_cols
|
|
69
|
+
data_widths.each_with_index do |width, idx|
|
|
70
|
+
next unless width > widths[idx]
|
|
71
|
+
|
|
72
|
+
allocated = [width, free_space].min
|
|
73
|
+
|
|
74
|
+
if allocated > 0
|
|
75
|
+
widths[idx] += allocated
|
|
76
|
+
free_space -= allocated
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
widths
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def max_width(data)
|
|
86
|
+
data.map do |value|
|
|
87
|
+
Array(value).join(", ").length
|
|
88
|
+
end.max
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def separator_row(col_widths)
|
|
92
|
+
sep = "#{borders[:horizontal]}#{borders[:vertical]}#{borders[:horizontal]}"
|
|
93
|
+
|
|
94
|
+
"#{borders[:corner]}#{borders[:horizontal]}" +
|
|
95
|
+
col_widths.map { |width| borders[:horizontal] * width }.join(sep) +
|
|
96
|
+
"#{borders[:horizontal]}#{borders[:corner]}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def split_rows(row, col_widths)
|
|
100
|
+
return [] unless row.find { |v| v && v.length > 0 }
|
|
101
|
+
|
|
102
|
+
rows_with_splits = [row]
|
|
103
|
+
next_row = []
|
|
104
|
+
|
|
105
|
+
joined_data = row.each_with_index.map do |val, idx|
|
|
106
|
+
words = Array(val).map(&:to_s)
|
|
107
|
+
target_width = col_widths[idx]
|
|
108
|
+
|
|
109
|
+
(cell, remainder) = row_and_remainder(words, target_width)
|
|
110
|
+
|
|
111
|
+
next_row[idx] = remainder
|
|
112
|
+
cell
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
([aligned_row(joined_data, col_widths)] +
|
|
116
|
+
split_rows(next_row, col_widths)).compact
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def row_and_remainder(words, target_width)
|
|
120
|
+
if(words.join(", ").length < target_width)
|
|
121
|
+
return [words.join(", "), nil]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
this_row = []
|
|
125
|
+
while words.length > 0 && (this_row.join(", ").length + words[0].length) <= target_width
|
|
126
|
+
this_row << words.shift
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
[this_row.join(", "), words]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def aligned_row(row, col_widths)
|
|
133
|
+
aligned_values = row.each_with_index.map do |data, idx|
|
|
134
|
+
if idx == 0
|
|
135
|
+
data.rjust(col_widths[idx])
|
|
136
|
+
else
|
|
137
|
+
data.ljust(col_widths[idx])
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
"#{borders[:vertical]} " + aligned_values.join(" #{borders[:vertical]} ") + " #{borders[:vertical]}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
require 'bundler/stats'
|
|
3
|
+
|
|
4
|
+
describe Bundler::Stats::Printer do
|
|
5
|
+
subject { described_class }
|
|
6
|
+
|
|
7
|
+
def set_term_width(width)
|
|
8
|
+
allow(Kernel).to receive(:"`").and_return(width)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
describe "#terminal_width" do
|
|
12
|
+
context "*nix systems" do
|
|
13
|
+
it "return the kernel width" do
|
|
14
|
+
set_term_width(180)
|
|
15
|
+
|
|
16
|
+
printer = subject.new
|
|
17
|
+
response = printer.terminal_width
|
|
18
|
+
|
|
19
|
+
expect(response).to eq(180)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "non-*nix systems" do
|
|
24
|
+
it "always returns a small number" do
|
|
25
|
+
set_term_width(nil)
|
|
26
|
+
|
|
27
|
+
printer = subject.new
|
|
28
|
+
response = printer.terminal_width
|
|
29
|
+
|
|
30
|
+
expect(response).to eq(80)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "#column_widths" do
|
|
36
|
+
it "comfortably prints tables of comfortable data" do
|
|
37
|
+
set_term_width(80)
|
|
38
|
+
printer = subject.new
|
|
39
|
+
table_data = [
|
|
40
|
+
[ "name", "data" ],
|
|
41
|
+
[ "*" * 10, "*" * 20 ],
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
widths = printer.column_widths(table_data)
|
|
45
|
+
|
|
46
|
+
expect(widths).to eq([10, 20])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "smooshes uncomfortably long data" do
|
|
50
|
+
set_term_width(60)
|
|
51
|
+
printer = subject.new
|
|
52
|
+
table_data = [
|
|
53
|
+
[ "name", "data" ],
|
|
54
|
+
[ "*" * 10, "*" * 60 ],
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
widths = printer.column_widths(table_data)
|
|
58
|
+
|
|
59
|
+
target_widths = [10, 43] # 7 for gutters
|
|
60
|
+
expect(widths).to eq(target_widths)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "always allows some amount of space for data" do
|
|
64
|
+
set_term_width(60)
|
|
65
|
+
printer = subject.new
|
|
66
|
+
table_data = [
|
|
67
|
+
[ "name", "data1", "data2", "data3" ],
|
|
68
|
+
[ "*" * 10, "*" * 60, "*" * 60, "*" * 60 ],
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
widths = printer.column_widths(table_data)
|
|
72
|
+
|
|
73
|
+
target_widths = [10, 17, 10, 10] # 13 for gutters
|
|
74
|
+
expect(widths).to eq(target_widths)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "bails if it can't handle that data" do
|
|
78
|
+
set_term_width(10)
|
|
79
|
+
printer = subject.new
|
|
80
|
+
table_data = [
|
|
81
|
+
[ "name", "data" ],
|
|
82
|
+
[ "*" * 10, "*" * 20 ],
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
expect {
|
|
86
|
+
printer.column_widths(table_data)
|
|
87
|
+
}.to raise_error(ArgumentError)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe "#to_s" do
|
|
92
|
+
it "prints a pretty table" do
|
|
93
|
+
set_term_width(80)
|
|
94
|
+
printer = subject.new(headers: ["stars", "stripes"],
|
|
95
|
+
data: [["*****", "*****"]]
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
output = printer.to_s
|
|
99
|
+
table = <<-TABLE
|
|
100
|
+
+-------|---------+
|
|
101
|
+
| stars | stripes |
|
|
102
|
+
+-------|---------+
|
|
103
|
+
| ***** | ***** |
|
|
104
|
+
+-------|---------+
|
|
105
|
+
TABLE
|
|
106
|
+
|
|
107
|
+
expect(output).to eq(table.chomp)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "deals with data alignment" do
|
|
111
|
+
set_term_width(80)
|
|
112
|
+
printer = subject.new(headers: ["name", "value"],
|
|
113
|
+
data: [
|
|
114
|
+
["one", "*****"],
|
|
115
|
+
["seventeen", "///////////////"]
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
output = printer.to_s
|
|
119
|
+
table = <<-TABLE
|
|
120
|
+
+-----------|-----------------+
|
|
121
|
+
| name | value |
|
|
122
|
+
+-----------|-----------------+
|
|
123
|
+
| one | ***** |
|
|
124
|
+
| seventeen | /////////////// |
|
|
125
|
+
+-----------|-----------------+
|
|
126
|
+
TABLE
|
|
127
|
+
|
|
128
|
+
expect(output).to eq(table.chomp)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "wraps data as necessary" do
|
|
132
|
+
set_term_width(35)
|
|
133
|
+
printer = subject.new(headers: ["name", "value"],
|
|
134
|
+
data: [
|
|
135
|
+
["words", ["one", "two", "three", "four", "five"]],
|
|
136
|
+
])
|
|
137
|
+
|
|
138
|
+
output = printer.to_s
|
|
139
|
+
table = <<-TABLE
|
|
140
|
+
+------------|--------------------+
|
|
141
|
+
| name | value |
|
|
142
|
+
+------------|--------------------+
|
|
143
|
+
| words | one, two, three |
|
|
144
|
+
| | four, five |
|
|
145
|
+
+------------|--------------------+
|
|
146
|
+
TABLE
|
|
147
|
+
|
|
148
|
+
expect(output).to eq(table.chomp)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "can wrap multiple columns" do
|
|
152
|
+
set_term_width(45)
|
|
153
|
+
printer = subject.new(headers: ["name", "value", "other value"],
|
|
154
|
+
data: [
|
|
155
|
+
[ "words",
|
|
156
|
+
["one", "two", "three", "four", "five"],
|
|
157
|
+
["six", "seven", "eight", "nine", "ten"],
|
|
158
|
+
]
|
|
159
|
+
])
|
|
160
|
+
|
|
161
|
+
output = printer.to_s
|
|
162
|
+
table = <<-TABLE
|
|
163
|
+
+------------|-----------------|------------+
|
|
164
|
+
| name | value | other value |
|
|
165
|
+
+------------|-----------------|------------+
|
|
166
|
+
| words | one, two, three | six, seven |
|
|
167
|
+
| | four, five | eight, nine |
|
|
168
|
+
| | | ten |
|
|
169
|
+
+------------|-----------------|------------+
|
|
170
|
+
TABLE
|
|
171
|
+
|
|
172
|
+
expect(output).to eq(table.chomp)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "can print without a header" do
|
|
176
|
+
set_term_width(80)
|
|
177
|
+
printer = subject.new(headers: nil,
|
|
178
|
+
data: [
|
|
179
|
+
["*****", "********"],
|
|
180
|
+
["++++++++", "+++++"],
|
|
181
|
+
])
|
|
182
|
+
|
|
183
|
+
output = printer.to_s
|
|
184
|
+
table = <<-TABLE
|
|
185
|
+
+----------|----------+
|
|
186
|
+
| ***** | ******** |
|
|
187
|
+
| ++++++++ | +++++ |
|
|
188
|
+
+----------|----------+
|
|
189
|
+
TABLE
|
|
190
|
+
|
|
191
|
+
expect(output).to eq(table.chomp)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "can print without separators at all!" do
|
|
195
|
+
set_term_width(80)
|
|
196
|
+
printer = subject.new(headers: nil,
|
|
197
|
+
borders: false,
|
|
198
|
+
data: [
|
|
199
|
+
["*****", "********"],
|
|
200
|
+
["++++++++", "+++++"],
|
|
201
|
+
])
|
|
202
|
+
|
|
203
|
+
output = printer.to_s
|
|
204
|
+
table = <<-TABLE
|
|
205
|
+
|
|
206
|
+
***** ********
|
|
207
|
+
++++++++ +++++
|
|
208
|
+
|
|
209
|
+
TABLE
|
|
210
|
+
|
|
211
|
+
expect(output).to eq(table.chomp)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bundler-stats
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joseph Mastey
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-
|
|
11
|
+
date: 2019-05-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -66,6 +66,20 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '2.13'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: guard-rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
84
|
name: pry
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,6 +94,20 @@ dependencies:
|
|
|
80
94
|
- - "~>"
|
|
81
95
|
- !ruby/object:Gem::Version
|
|
82
96
|
version: '0.10'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rb-readline
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
83
111
|
description: Looks through your lockfile and tries to identify problematic use of
|
|
84
112
|
dependencies
|
|
85
113
|
email: jmmastey@gmail.com
|
|
@@ -93,6 +121,7 @@ extra_rdoc_files:
|
|
|
93
121
|
- README.md
|
|
94
122
|
files:
|
|
95
123
|
- ".gitignore"
|
|
124
|
+
- ".rspec"
|
|
96
125
|
- ".travis.yml"
|
|
97
126
|
- CHANGELOG.md
|
|
98
127
|
- CODE_OF_CONDUCT.md
|
|
@@ -106,10 +135,12 @@ files:
|
|
|
106
135
|
- lib/bundler/stats.rb
|
|
107
136
|
- lib/bundler/stats/calculator.rb
|
|
108
137
|
- lib/bundler/stats/cli.rb
|
|
138
|
+
- lib/bundler/stats/printer.rb
|
|
109
139
|
- lib/bundler/stats/remover.rb
|
|
110
140
|
- lib/bundler/stats/tree.rb
|
|
111
141
|
- lib/bundler/stats/version.rb
|
|
112
142
|
- spec/lib/bundler/stats/calculator_spec.rb
|
|
143
|
+
- spec/lib/bundler/stats/printer_spec.rb
|
|
113
144
|
- spec/lib/bundler/stats/remover_spec.rb
|
|
114
145
|
- spec/lib/bundler/stats/tree_spec.rb
|
|
115
146
|
- spec/test_gemfile
|
|
@@ -137,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
137
168
|
version: '0'
|
|
138
169
|
requirements: []
|
|
139
170
|
rubyforge_project:
|
|
140
|
-
rubygems_version: 2.
|
|
171
|
+
rubygems_version: 2.7.3
|
|
141
172
|
signing_key:
|
|
142
173
|
specification_version: 4
|
|
143
174
|
summary: Dependency investigation for Bundler
|