libyear-bundler 0.8.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e363af1df56aed65c6977f010e31514cc9e57e3b4270eacecff7005033554a3
4
- data.tar.gz: 9293a7bc0a6708685577e28d85f43efeb15c3031f5432277be2fc593391a2d62
3
+ metadata.gz: '0598b786c916a1f5645f5785f9e71b7dee517a908abd24410ebd5c9262e1681a'
4
+ data.tar.gz: 78396f8f657749c1206f76b7ab7d47a1ed6f182f9aefcba0ed478dfa72b3a83d
5
5
  SHA512:
6
- metadata.gz: 4bed9a52de479d8a0479846cfa26beaf71b48c4df930a2e1bd86a021fabbb3b755e33148e6d2fcda14ab6297d304ade51331f38074d6e7a45ddab7f476ee0cb7
7
- data.tar.gz: d190eb7367ccc794de8ba33ecf5794cb86b431a8b9227975d28c93f08f9930c1fd92e555e2280f720b9153647308a57694c2163a1599d43449b212e6ed56448c
6
+ metadata.gz: 8a9c45f47f10127b72ee0bfc4a38d52e3fff677b55d93d8f125730b5a3bf1f9a8920590667465af4388ad14cbeda33c37852a51729497a0740d003b326057797
7
+ data.tar.gz: 38610ebd31b56aa6173b2ff861756fa602bbefe63855de0ec4d21d2940e0292278ae3f82ad2ff08633813ffce7c890b88a8a20341962f54b3354f5c4e599c03c
@@ -17,21 +17,25 @@ module LibyearBundler
17
17
  end
18
18
 
19
19
  def execute
20
- bundle_outdated.lines.each_with_object([]) do |line, gems|
21
- match = BOP_FMT.match(line)
22
- next if match.nil?
23
- if malformed_version_strings?(match)
24
- warn "Skipping #{match['name']} because of a malformed version string"
25
- next
26
- end
20
+ uri = URI('https://rubygems.org')
21
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
22
+ bundle_outdated.lines.each_with_object([]) do |line, gems|
23
+ match = BOP_FMT.match(line)
24
+ next if match.nil?
25
+ if malformed_version_strings?(match)
26
+ warn "Skipping #{match['name']} because of a malformed version string"
27
+ next
28
+ end
27
29
 
28
- gem = ::LibyearBundler::Models::Gem.new(
29
- match['name'],
30
- match['installed'],
31
- match['newest'],
32
- @release_date_cache
33
- )
34
- gems.push(gem)
30
+ gem = ::LibyearBundler::Models::Gem.new(
31
+ match['name'],
32
+ match['installed'],
33
+ match['newest'],
34
+ @release_date_cache,
35
+ http
36
+ )
37
+ gems.push(gem)
38
+ end
35
39
  end
36
40
  end
37
41
 
@@ -3,7 +3,8 @@ require "bundler/cli/outdated"
3
3
  require "libyear_bundler/bundle_outdated"
4
4
  require "libyear_bundler/options"
5
5
  require "libyear_bundler/release_date_cache"
6
- require "libyear_bundler/report"
6
+ require "libyear_bundler/reports/console"
7
+ require "libyear_bundler/reports/json"
7
8
  require 'libyear_bundler/models/ruby'
8
9
 
9
10
  module LibyearBundler
@@ -73,7 +74,10 @@ module LibyearBundler
73
74
  end
74
75
 
75
76
  def report
76
- @_report ||= Report.new(bundle_outdated, ruby, @options, $stdout)
77
+ @_report ||= begin
78
+ reporter_class = @options.json? ? Reports::JSON : Reports::Console
79
+ reporter_class.new(bundle_outdated, ruby, @options, $stdout)
80
+ end
77
81
  end
78
82
 
79
83
  def ruby
@@ -7,7 +7,7 @@ module LibyearBundler
7
7
  # Logic and information pertaining to the installed and newest versions of
8
8
  # a gem
9
9
  class Gem
10
- def initialize(name, installed_version, newest_version, release_date_cache)
10
+ def initialize(name, installed_version, newest_version, release_date_cache, http)
11
11
  unless release_date_cache.nil? || release_date_cache.is_a?(ReleaseDateCache)
12
12
  raise TypeError, 'Invalid release_date_cache'
13
13
  end
@@ -15,29 +15,28 @@ module LibyearBundler
15
15
  @installed_version = installed_version
16
16
  @newest_version = newest_version
17
17
  @release_date_cache = release_date_cache
18
+ @http = http
18
19
  end
19
20
 
20
21
  class << self
21
- def release_date(gem_name, gem_version)
22
- dep = nil
23
- begin
24
- dep = ::Bundler::Dependency.new(gem_name, gem_version)
25
- rescue ::Gem::Requirement::BadRequirementError => e
26
- report_problem(gem_name, <<-MSG)
27
- Could not find release date for: #{gem_name}
28
- #{e}
29
- Maybe you used git in your Gemfile, which libyear doesn't support yet. Contributions welcome.
30
- MSG
31
- return nil
22
+ def release_date(gem_name, gem_version, http)
23
+ uri = URI.parse(
24
+ "https://rubygems.org/api/v2/rubygems/#{gem_name}/versions/#{gem_version}.json"
25
+ )
26
+ request = Net::HTTP::Get.new(uri)
27
+ response = http.request(request)
28
+ if response.is_a?(Net::HTTPSuccess)
29
+ parsed_response = JSON.parse(response.body)
30
+ Date.parse(parsed_response["version_created_at"])
31
+ else
32
+ report_problem(
33
+ gem_name,
34
+ "Release date not found: #{gem_name}: rubygems.org responded with #{response.code}"
35
+ )
36
+ nil
32
37
  end
33
- tuples, _errors = ::Gem::SpecFetcher.fetcher.search_for_dependency(dep)
34
- if tuples.empty?
35
- report_problem(gem_name, "Could not find release date for: #{gem_name}")
36
- return nil
37
- end
38
- tup, source = tuples.first # Gem::NameTuple
39
- spec = source.fetch_spec(tup) # raises Gem::RemoteFetcher::FetchError
40
- spec.date.to_date
38
+ rescue StandardError => e
39
+ report_problem(gem_name, "Release date not found: #{gem_name}: #{e.inspect}")
41
40
  end
42
41
 
43
42
  private
@@ -57,7 +56,7 @@ Maybe you used git in your Gemfile, which libyear doesn't support yet. Contribut
57
56
 
58
57
  def installed_version_release_date
59
58
  if @release_date_cache.nil?
60
- self.class.release_date(name, installed_version)
59
+ self.class.release_date(name, installed_version, @http)
61
60
  else
62
61
  @release_date_cache[name, installed_version]
63
62
  end
@@ -88,7 +87,7 @@ Maybe you used git in your Gemfile, which libyear doesn't support yet. Contribut
88
87
 
89
88
  def newest_version_release_date
90
89
  if @release_date_cache.nil?
91
- self.class.release_date(name, newest_version)
90
+ self.class.release_date(name, newest_version, @http)
92
91
  else
93
92
  @release_date_cache[name, newest_version]
94
93
  end
@@ -115,7 +114,8 @@ Maybe you used git in your Gemfile, which libyear doesn't support yet. Contribut
115
114
  def versions_sequence
116
115
  @_versions_sequence ||= begin
117
116
  uri = URI.parse("https://rubygems.org/api/v1/versions/#{name}.json")
118
- response = Net::HTTP.get_response(uri)
117
+ request = Net::HTTP::Get.new(uri)
118
+ response = @http.request(request)
119
119
  parsed_response = JSON.parse(response.body)
120
120
  parsed_response.map { |version| version['number'] }
121
121
  end
@@ -62,7 +62,7 @@ module LibyearBundler
62
62
 
63
63
  # YAML#safe_load provides an already-parsed Date object, so the following
64
64
  # is a Date object
65
- v['date']
65
+ @_version_data[v]['date']
66
66
  end
67
67
 
68
68
  private
@@ -84,7 +84,9 @@ module LibyearBundler
84
84
  con.request_get(uri.path)
85
85
  end
86
86
  if response.is_a?(::Net::HTTPSuccess)
87
- YAMLLoader.safe_load(response.body).map { |release| release['version'] }
87
+ @_version_data = YAMLLoader.safe_load(response.body)
88
+ .map { |release| [release['version'], release] }.to_h
89
+ @_version_data.keys
88
90
  else
89
91
  warn format('Unable to get Ruby version list: response code: %s', response.code)
90
92
  []
@@ -1,7 +1,6 @@
1
1
  require 'optparse'
2
2
  require 'libyear_bundler/version'
3
3
  require "libyear_bundler/cli"
4
- require 'ostruct'
5
4
 
6
5
  module LibyearBundler
7
6
  # Uses OptionParser from Ruby's stdlib to hand command-line arguments
@@ -11,14 +10,18 @@ Usage: libyear-bundler [Gemfile ...] [options]
11
10
  https://github.com/jaredbeck/libyear-bundler/
12
11
  BANNER
13
12
 
13
+ Store = Struct.new(
14
+ :libyears?, :releases?, :versions?, :cache_path, :grand_total?, :sort?, :json?
15
+ )
16
+
14
17
  def initialize(argv)
15
18
  @argv = argv
16
- @options = ::OpenStruct.new
19
+ @options = Store.new
17
20
  @optparser = OptionParser.new do |opts|
18
21
  opts.banner = BANNER
19
22
  opts.program_name = 'libyear-bundler'
20
23
  opts.version = ::LibyearBundler::VERSION
21
- @options.send('libyears?=', true)
24
+ @options.send(:'libyears?=', true)
22
25
 
23
26
  opts.on_head('-h', '--help', 'Prints this help') do
24
27
  puts opts
@@ -26,9 +29,9 @@ https://github.com/jaredbeck/libyear-bundler/
26
29
  end
27
30
 
28
31
  opts.on('--all', 'Calculate all metrics') do
29
- @options.send('libyears?=', true)
30
- @options.send('releases?=', true)
31
- @options.send('versions?=', true)
32
+ @options.send(:'libyears?=', true)
33
+ @options.send(:'releases?=', true)
34
+ @options.send(:'versions?=', true)
32
35
  end
33
36
 
34
37
  opts.on('--cache=CACHE_PATH', 'Use a cache across runs') do |cache_path|
@@ -36,25 +39,29 @@ https://github.com/jaredbeck/libyear-bundler/
36
39
  end
37
40
 
38
41
  opts.on('--libyears', '[default] Calculate libyears out-of-date') do
39
- @options.send('libyears?=', true)
42
+ @options.send(:'libyears?=', true)
40
43
  end
41
44
 
42
45
  opts.on('--releases', 'Calculate number of releases out-of-date') do
43
- @options.send('libyears?=', false)
44
- @options.send('releases?=', true)
46
+ @options.send(:'libyears?=', false)
47
+ @options.send(:'releases?=', true)
45
48
  end
46
49
 
47
50
  opts.on('--versions', 'Calculate major, minor, and patch versions out-of-date') do
48
- @options.send('libyears?=', false)
49
- @options.send('versions?=', true)
51
+ @options.send(:'libyears?=', false)
52
+ @options.send(:'versions?=', true)
50
53
  end
51
54
 
52
55
  opts.on('--grand-total', 'Return value for given metric(s)') do
53
- @options.send('grand_total?=', true)
56
+ @options.send(:'grand_total?=', true)
54
57
  end
55
58
 
56
59
  opts.on('--sort', 'Sort by selected metric(s), in descending order') do
57
- @options.send('sort?=', true)
60
+ @options.send(:'sort?=', true)
61
+ end
62
+
63
+ opts.on('--json', 'Output JSON') do
64
+ @options.send(:'json?=', true)
58
65
  end
59
66
  end
60
67
  end
@@ -8,6 +8,8 @@ module LibyearBundler
8
8
  def initialize(data)
9
9
  raise TypeError unless data.is_a?(Hash)
10
10
  @data = data
11
+ uri = URI('https://rubygems.org')
12
+ @http = Net::HTTP.start(uri.host, uri.port, use_ssl: true)
11
13
  end
12
14
 
13
15
  def [](name, version)
@@ -52,7 +54,7 @@ module LibyearBundler
52
54
  if name == 'ruby'
53
55
  Models::Ruby.release_date(version)
54
56
  else
55
- Models::Gem.release_date(name, version)
57
+ Models::Gem.release_date(name, version, @http)
56
58
  end
57
59
  end
58
60
  end
@@ -0,0 +1,87 @@
1
+ module LibyearBundler
2
+ module Reports
3
+ # Base class for all reporters. Should only be concerned with presentation, nothing else.
4
+ #
5
+ # Subclasses should implement the `#write` method.
6
+ class Base
7
+ # `gems` - Array of `::LibyearBundler::Models::Gem` instances
8
+ # `options` - Instance of `::LibyearBundler::Options`
9
+ def initialize(gems, ruby, options, io)
10
+ @gems = gems
11
+ @ruby = ruby
12
+ @options = options
13
+ @io = io
14
+ end
15
+
16
+ def write
17
+ raise NoMethodError, "Implement in subclass"
18
+ end
19
+
20
+ def to_h
21
+ @_to_h ||=
22
+ begin
23
+ gems = sorted_gems(@gems)
24
+ summary = {
25
+ gems: gems,
26
+ sum_libyears: 0.0
27
+ }
28
+ gems.each { |gem| increment_metrics_summary(gem, summary) }
29
+
30
+ begin
31
+ increment_metrics_summary(@ruby, summary) if @ruby.outdated?
32
+ rescue StandardError => e
33
+ warn "Unable to calculate libyears for ruby itself: #{e}"
34
+ end
35
+
36
+ summary
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def sorted_gems(gems)
43
+ if @options.sort?
44
+ gems.sort_by do |gem|
45
+ [
46
+ (gem.libyears if @options.libyears?),
47
+ (gem.version_sequence_delta if @options.releases?),
48
+ (gem.version_number_delta if @options.versions?)
49
+ ].compact
50
+ end.reverse
51
+ else
52
+ gems
53
+ end
54
+ end
55
+
56
+ def increment_metrics_summary(model, summary)
57
+ increment_libyears(model, summary) if @options.libyears?
58
+ increment_version_deltas(model, summary) if @options.versions?
59
+ increment_seq_deltas(model, summary) if @options.releases?
60
+ end
61
+
62
+ def increment_libyears(model, memo)
63
+ memo[:sum_libyears] += model.libyears
64
+ rescue StandardError => e
65
+ warn "Unable to calculate libyears for #{model.name}: #{e}"
66
+ end
67
+
68
+ def increment_seq_deltas(model, memo)
69
+ memo[:sum_seq_delta] ||= 0
70
+ memo[:sum_seq_delta] += model.version_sequence_delta
71
+ rescue StandardError => e
72
+ warn "Unable to calculate seq deltas for #{model.name}: #{e}"
73
+ end
74
+
75
+ def increment_version_deltas(model, memo)
76
+ memo[:sum_major_version] ||= 0
77
+ memo[:sum_major_version] += model.version_number_delta[0]
78
+ memo[:sum_minor_version] ||= 0
79
+ memo[:sum_minor_version] += model.version_number_delta[1]
80
+ memo[:sum_patch_version] ||= 0
81
+ memo[:sum_patch_version] += model.version_number_delta[2]
82
+ rescue StandardError => e
83
+ warn "Unable to calculate version deltas for #{model.name}: #{e}"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,102 @@
1
+ require "libyear_bundler/reports/base"
2
+
3
+ module LibyearBundler
4
+ module Reports
5
+ # Responsible presenting data from the `::LibyearBundler::Models`. Should only
6
+ # be concerned with presentation, nothing else.
7
+ class Console < Base
8
+ FMT_LIBYEARS_COLUMN = "%10.1f".freeze
9
+ FMT_RELEASES_COLUMN = "%10d".freeze
10
+ FMT_VERSIONS_COLUMN = "%15s".freeze
11
+ FMT_SUMMARY_COLUMNS = "%30s%15s%15s%15s%15s".freeze
12
+
13
+ def write
14
+ to_h[:gems].each { |gem| put_line_summary(gem) }
15
+
16
+ begin
17
+ put_line_summary(@ruby) if @ruby.outdated?
18
+ rescue StandardError => e
19
+ warn "Unable to calculate libyears for ruby itself: #{e} (line summary)"
20
+ end
21
+
22
+ put_summary(to_h)
23
+ end
24
+
25
+ private
26
+
27
+ def put_line_summary(gem_or_ruby)
28
+ meta = meta_line_summary(gem_or_ruby)
29
+
30
+ if @options.releases?
31
+ releases = format(FMT_RELEASES_COLUMN, gem_or_ruby.version_sequence_delta)
32
+ meta << releases
33
+ end
34
+
35
+ if @options.versions?
36
+ versions = format(FMT_VERSIONS_COLUMN, gem_or_ruby.version_number_delta)
37
+ meta << versions
38
+ end
39
+
40
+ if @options.libyears?
41
+ libyears = format(FMT_LIBYEARS_COLUMN, gem_or_ruby.libyears)
42
+ meta << libyears
43
+ end
44
+
45
+ @io.puts meta
46
+ end
47
+
48
+ def meta_line_summary(gem_or_ruby)
49
+ format(
50
+ FMT_SUMMARY_COLUMNS,
51
+ gem_or_ruby.name,
52
+ gem_or_ruby.installed_version.to_s,
53
+ gem_or_ruby.installed_version_release_date,
54
+ gem_or_ruby.newest_version.to_s,
55
+ gem_or_ruby.newest_version_release_date
56
+ )
57
+ end
58
+
59
+ def put_libyear_summary(sum_libyears)
60
+ @io.puts format("System is %.1f libyears behind", sum_libyears)
61
+ end
62
+
63
+ def put_version_delta_summary(sum_major_version, sum_minor_version, sum_patch_version)
64
+ @io.puts format(
65
+ "Major, minor, patch versions behind: %<major>d, %<minor>d, %<patch>d",
66
+ major: sum_major_version || 0,
67
+ minor: sum_minor_version || 0,
68
+ patch: sum_patch_version || 0
69
+ )
70
+ end
71
+
72
+ def put_sum_seq_delta_summary(sum_seq_delta)
73
+ @io.puts format(
74
+ "Total releases behind: %<seq_delta>d",
75
+ seq_delta: sum_seq_delta || 0
76
+ )
77
+ end
78
+
79
+ def put_summary(summary)
80
+ if [:libyears?, :releases?, :versions?].all? { |opt| @options.send(opt) }
81
+ put_libyear_summary(summary[:sum_libyears])
82
+ put_sum_seq_delta_summary(summary[:sum_seq_delta])
83
+ put_version_delta_summary(
84
+ summary[:sum_major_version],
85
+ summary[:sum_minor_version],
86
+ summary[:sum_patch_version]
87
+ )
88
+ elsif @options.versions?
89
+ put_version_delta_summary(
90
+ summary[:sum_major_version],
91
+ summary[:sum_minor_version],
92
+ summary[:sum_patch_version]
93
+ )
94
+ elsif @options.releases?
95
+ put_sum_seq_delta_summary(summary[:sum_seq_delta])
96
+ elsif @options.libyears?
97
+ put_libyear_summary(summary[:sum_libyears])
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,52 @@
1
+ require 'json'
2
+ require 'libyear_bundler/reports/base'
3
+
4
+ module LibyearBundler
5
+ module Reports
6
+ # Responsible for generating data from the `::LibyearBundler::Models` in JSON format.
7
+ # Should only be concerned with presentation, nothing else.
8
+ class JSON < Base
9
+ def write
10
+ data = {
11
+ gems: to_h[:gems].map { |gem| gem_info(gem) },
12
+ ruby: gem_info(@ruby)
13
+ }
14
+ data[:sum_libyears] = to_h[:sum_libyears].round(1) if @options.libyears?
15
+ data[:sum_seq_delta] = to_h[:sum_seq_delta].round(1) if @options.releases?
16
+ if @options.versions?
17
+ data[:sum_major_version] = to_h[:sum_major_version].round(1)
18
+ data[:sum_minor_version] = to_h[:sum_minor_version].round(1)
19
+ data[:sum_patch_version] = to_h[:sum_patch_version].round(1)
20
+ end
21
+
22
+ @io.puts ::JSON.pretty_generate(data)
23
+ end
24
+
25
+ private
26
+
27
+ def gem_info(gem_or_ruby)
28
+ info = {
29
+ name: gem_or_ruby.name,
30
+ installed_version: gem_or_ruby.installed_version.to_s,
31
+ installed_version_release_date: gem_or_ruby.installed_version_release_date,
32
+ newest_version: gem_or_ruby.newest_version.to_s,
33
+ newest_version_release_date: gem_or_ruby.newest_version_release_date
34
+ }
35
+
36
+ if @options.releases?
37
+ info[:releases] = gem_or_ruby.version_sequence_delta
38
+ end
39
+
40
+ if @options.versions?
41
+ info[:versions] = [:major, :minor, :patch].zip(gem_or_ruby.version_number_delta).to_h
42
+ end
43
+
44
+ if @options.libyears?
45
+ info[:libyears] = gem_or_ruby.libyears.round(1)
46
+ end
47
+
48
+ info
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,3 @@
1
1
  module LibyearBundler
2
- VERSION = "0.8.0".freeze
2
+ VERSION = "0.9.1".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libyear-bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jared Beck
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-09-14 00:00:00.000000000 Z
10
+ date: 2025-10-09 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -30,7 +29,6 @@ dependencies:
30
29
  - - "<"
31
30
  - !ruby/object:Gem::Version
32
31
  version: '3'
33
- description:
34
32
  email:
35
33
  - jared@jaredbeck.com
36
34
  executables:
@@ -50,7 +48,9 @@ files:
50
48
  - lib/libyear_bundler/models/ruby.rb
51
49
  - lib/libyear_bundler/options.rb
52
50
  - lib/libyear_bundler/release_date_cache.rb
53
- - lib/libyear_bundler/report.rb
51
+ - lib/libyear_bundler/reports/base.rb
52
+ - lib/libyear_bundler/reports/console.rb
53
+ - lib/libyear_bundler/reports/json.rb
54
54
  - lib/libyear_bundler/version.rb
55
55
  - lib/libyear_bundler/yaml_loader.rb
56
56
  - libyear-bundler.gemspec
@@ -58,7 +58,6 @@ homepage: https://libyear.com
58
58
  licenses:
59
59
  - GPL-3.0-only
60
60
  metadata: {}
61
- post_install_message:
62
61
  rdoc_options: []
63
62
  require_paths:
64
63
  - lib
@@ -73,8 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
72
  - !ruby/object:Gem::Version
74
73
  version: '0'
75
74
  requirements: []
76
- rubygems_version: 3.5.16
77
- signing_key:
75
+ rubygems_version: 3.6.2
78
76
  specification_version: 4
79
77
  summary: A simple measure of dependency freshness
80
78
  test_files: []
@@ -1,171 +0,0 @@
1
- module LibyearBundler
2
- # Responsible presenting data from the `::LibyearBundler::Models`. Should only
3
- # be concerned with presentation, nothing else.
4
- class Report
5
- FMT_LIBYEARS_COLUMN = "%10.1f".freeze
6
- FMT_RELEASES_COLUMN = "%10d".freeze
7
- FMT_VERSIONS_COLUMN = "%15s".freeze
8
- FMT_SUMMARY_COLUMNS = "%30s%15s%15s%15s%15s".freeze
9
-
10
- # `gems` - Array of `::LibyearBundler::Models::Gem` instances
11
- # `options` - Instance of `::LibyearBundler::Options`
12
- def initialize(gems, ruby, options, io)
13
- @gems = gems
14
- @ruby = ruby
15
- @options = options
16
- @io = io
17
- end
18
-
19
- def write
20
- to_h[:gems].each { |gem| put_line_summary(gem) }
21
-
22
- begin
23
- put_line_summary(@ruby) if @ruby.outdated?
24
- rescue StandardError => e
25
- warn "Unable to calculate libyears for ruby itself: #{e} (line summary)"
26
- end
27
-
28
- put_summary(to_h)
29
- end
30
-
31
- def to_h
32
- @_to_h ||=
33
- begin
34
- gems = sorted_gems(@gems)
35
- summary = {
36
- gems: gems,
37
- sum_libyears: 0.0
38
- }
39
- gems.each { |gem| increment_metrics_summary(gem, summary) }
40
-
41
- begin
42
- increment_metrics_summary(@ruby, summary) if @ruby.outdated?
43
- rescue StandardError => e
44
- warn "Unable to calculate libyears for ruby itself: #{e}"
45
- end
46
-
47
- summary
48
- end
49
- end
50
-
51
- private
52
-
53
- def sorted_gems(gems)
54
- if @options.sort?
55
- gems.sort_by do |gem|
56
- [
57
- (gem.libyears if @options.libyears?),
58
- (gem.version_sequence_delta if @options.releases?),
59
- (gem.version_number_delta if @options.versions?)
60
- ].compact
61
- end.reverse
62
- else
63
- gems
64
- end
65
- end
66
-
67
- def increment_metrics_summary(model, summary)
68
- increment_libyears(model, summary) if @options.libyears?
69
- increment_version_deltas(model, summary) if @options.versions?
70
- increment_seq_deltas(model, summary) if @options.releases?
71
- end
72
-
73
- def put_line_summary(gem_or_ruby)
74
- meta = meta_line_summary(gem_or_ruby)
75
-
76
- if @options.releases?
77
- releases = format(FMT_RELEASES_COLUMN, gem_or_ruby.version_sequence_delta)
78
- meta << releases
79
- end
80
-
81
- if @options.versions?
82
- versions = format(FMT_VERSIONS_COLUMN, gem_or_ruby.version_number_delta)
83
- meta << versions
84
- end
85
-
86
- if @options.libyears?
87
- libyears = format(FMT_LIBYEARS_COLUMN, gem_or_ruby.libyears)
88
- meta << libyears
89
- end
90
-
91
- @io.puts meta
92
- end
93
-
94
- def meta_line_summary(gem_or_ruby)
95
- format(
96
- FMT_SUMMARY_COLUMNS,
97
- gem_or_ruby.name,
98
- gem_or_ruby.installed_version.to_s,
99
- gem_or_ruby.installed_version_release_date,
100
- gem_or_ruby.newest_version.to_s,
101
- gem_or_ruby.newest_version_release_date
102
- )
103
- end
104
-
105
- def put_libyear_summary(sum_libyears)
106
- @io.puts format("System is %.1f libyears behind", sum_libyears)
107
- end
108
-
109
- def put_version_delta_summary(sum_major_version, sum_minor_version, sum_patch_version)
110
- @io.puts format(
111
- "Major, minor, patch versions behind: %<major>d, %<minor>d, %<patch>d",
112
- major: sum_major_version || 0,
113
- minor: sum_minor_version || 0,
114
- patch: sum_patch_version || 0
115
- )
116
- end
117
-
118
- def put_sum_seq_delta_summary(sum_seq_delta)
119
- @io.puts format(
120
- "Total releases behind: %<seq_delta>d",
121
- seq_delta: sum_seq_delta || 0
122
- )
123
- end
124
-
125
- def put_summary(summary)
126
- if [:libyears?, :releases?, :versions?].all? { |opt| @options.send(opt) }
127
- put_libyear_summary(summary[:sum_libyears])
128
- put_sum_seq_delta_summary(summary[:sum_seq_delta])
129
- put_version_delta_summary(
130
- summary[:sum_major_version],
131
- summary[:sum_minor_version],
132
- summary[:sum_patch_version]
133
- )
134
- elsif @options.versions?
135
- put_version_delta_summary(
136
- summary[:sum_major_version],
137
- summary[:sum_minor_version],
138
- summary[:sum_patch_version]
139
- )
140
- elsif @options.releases?
141
- put_sum_seq_delta_summary(summary[:sum_seq_delta])
142
- elsif @options.libyears?
143
- put_libyear_summary(summary[:sum_libyears])
144
- end
145
- end
146
-
147
- def increment_libyears(model, memo)
148
- memo[:sum_libyears] += model.libyears
149
- rescue StandardError => e
150
- warn "Unable to calculate libyears for #{model.name}: #{e}"
151
- end
152
-
153
- def increment_seq_deltas(model, memo)
154
- memo[:sum_seq_delta] ||= 0
155
- memo[:sum_seq_delta] += model.version_sequence_delta
156
- rescue StandardError => e
157
- warn "Unable to calculate seq deltas for #{model.name}: #{e}"
158
- end
159
-
160
- def increment_version_deltas(model, memo)
161
- memo[:sum_major_version] ||= 0
162
- memo[:sum_major_version] += model.version_number_delta[0]
163
- memo[:sum_minor_version] ||= 0
164
- memo[:sum_minor_version] += model.version_number_delta[1]
165
- memo[:sum_patch_version] ||= 0
166
- memo[:sum_patch_version] += model.version_number_delta[2]
167
- rescue StandardError => e
168
- warn "Unable to calculate version deltas for #{model.name}: #{e}"
169
- end
170
- end
171
- end