Skip to content

Commit

Permalink
Move bundle_report rails compatibility logic into a class (#137)
Browse files Browse the repository at this point in the history
* Move bundle_report rails compatibility logic into a class

I extracted it into a class because I think it will be easier to use the `incompatible_gems_by_state` data there.

Based on the code review

* Add incompatible with new compatible versions

* Update changelog

* Capture stdout to assert

Co-authored-by: Ariel <[email protected]>

---------

Co-authored-by: Ariel <[email protected]>
  • Loading branch information
JuanVqz and arielj authored Mar 7, 2025
1 parent aca1c12 commit 13181a7
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 67 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# main [(unreleased)](https://github.com/fastruby/next_rails/compare/v1.4.4...main)

- [BUGFIX: example](https://github.com/fastruby/next_rails/pull/<number>)
- [Move rails_version compatibility to its own class](https://github.com/fastruby/next_rails/pull/137)

* Your changes/patches go here.

Expand Down
4 changes: 2 additions & 2 deletions exe/bundle_report
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ at_exit do
when "outdated" then NextRails::BundleReport.outdated(options.fetch(:format, nil))
else
if options[:ruby_version]
NextRails::BundleReport.compatibility(ruby_version: options.fetch(:ruby_version, "2.3"))
NextRails::BundleReport.ruby_compatibility(ruby_version: options.fetch(:ruby_version, "2.3"))
else
NextRails::BundleReport.compatibility(rails_version: options.fetch(:rails_version, "5.0"), include_rails_gems: options.fetch(:include_rails_gems, false))
NextRails::BundleReport.rails_compatibility(rails_version: options.fetch(:rails_version, "5.0"), include_rails_gems: options.fetch(:include_rails_gems, false))
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/next_rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "next_rails/init"
require "next_rails/bundle_report"
require "next_rails/bundle_report/ruby_version_compatibility"
require "next_rails/bundle_report/rails_version_compatibility"
require "deprecation_tracker"

module NextRails
Expand Down
67 changes: 8 additions & 59 deletions lib/next_rails/bundle_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,69 +8,18 @@ module NextRails
module BundleReport
extend self

def compatibility(rails_version: nil, ruby_version: nil, include_rails_gems: nil)
return puts RubyVersionCompatibility.new(options: { ruby_version: ruby_version }).generate if ruby_version
def ruby_compatibility(ruby_version: nil)
return unless ruby_version

incompatible_gems = NextRails::GemInfo.all.reject do |gem|
gem.compatible_with_rails?(rails_version: rails_version) || (!include_rails_gems && gem.from_rails?)
end.sort_by { |gem| gem.name }

incompatible_gems.each { |gem| gem.find_latest_compatible(rails_version: rails_version) }

incompatible_gems_by_state = incompatible_gems.group_by { |gem| gem.state(rails_version) }

puts erb_output(incompatible_gems_by_state, incompatible_gems, rails_version)
options = { ruby_version: ruby_version }
puts RubyVersionCompatibility.new(options: options).generate
end

def erb_output(incompatible_gems_by_state, incompatible_gems, rails_version)
template = <<-ERB
<% if incompatible_gems_by_state[:found_compatible] -%>
<%= Rainbow("=> Incompatible with Rails #{rails_version} (with new versions that are compatible):").white.bold %>
<%= Rainbow("These gems will need to be upgraded before upgrading to Rails #{rails_version}.").italic %>
<% incompatible_gems_by_state[:found_compatible].each do |gem| -%>
<%= gem_header(gem) %> - upgrade to <%= gem.latest_compatible_version.version %>
<% end -%>
<% end -%>
<% if incompatible_gems_by_state[:incompatible] -%>
<%= Rainbow("=> Incompatible with Rails #{rails_version} (with no new compatible versions):").white.bold %>
<%= Rainbow("These gems will need to be removed or replaced before upgrading to Rails #{rails_version}.").italic %>
<% incompatible_gems_by_state[:incompatible].each do |gem| -%>
<%= gem_header(gem) %> - new version, <%= gem.latest_version.version %>, is not compatible with Rails #{rails_version}
<% end -%>
<% end -%>
<% if incompatible_gems_by_state[:no_new_version] -%>
<%= Rainbow("=> Incompatible with Rails #{rails_version} (with no new versions):").white.bold %>
<%= Rainbow("These gems will need to be upgraded by us or removed before upgrading to Rails #{rails_version}.").italic %>
<%= Rainbow("This list is likely to contain internal gems, like Cuddlefish.").italic %>
<% incompatible_gems_by_state[:no_new_version].each do |gem| -%>
<%= gem_header(gem) %> - new version not found
<% end -%>
<% end -%>
<%= Rainbow(incompatible_gems.length.to_s).red %> gems incompatible with Rails <%= rails_version %>
ERB

erb_version = ERB.version
if erb_version =~ /erb.rb \[([\d\.]+) .*\]/
erb_version = $1
end

if Gem::Version.new(erb_version) < Gem::Version.new("2.2")
ERB.new(template, nil, "-").result(binding)
else
ERB.new(template, trim_mode: "-").result(binding)
end
end
def rails_compatibility(rails_version: nil, include_rails_gems: nil)
return unless rails_version

def gem_header(_gem)
header = Rainbow("#{_gem.name} #{_gem.version}").bold
header << Rainbow(" (loaded from git)").magenta if _gem.sourced_from_git?
header
options = { rails_version: rails_version, include_rails_gems: include_rails_gems }
puts RailsVersionCompatibility.new(options: options).generate
end

def compatible_ruby_version(rails_version)
Expand Down
84 changes: 84 additions & 0 deletions lib/next_rails/bundle_report/rails_version_compatibility.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
class NextRails::BundleReport::RailsVersionCompatibility
def initialize(gems: NextRails::GemInfo.all, options: {})
@gems = gems
@options = options
end

def generate
erb_output
end

def incompatible_gems_by_state
@incompatible_gems_by_state ||= begin
incompatible_gems.each { |gem| gem.find_latest_compatible(rails_version: rails_version) }
incompatible_gems.group_by { |gem| gem.state(rails_version) }
end
end

private

def erb_output
template = <<-ERB
<% if incompatible_gems_by_state[:found_compatible] -%>
<%= Rainbow("=> Incompatible with Rails #{rails_version} (with new versions that are compatible):").white.bold %>
<%= Rainbow("These gems will need to be upgraded before upgrading to Rails #{rails_version}.").italic %>
<% incompatible_gems_by_state[:found_compatible].each do |gem| -%>
<%= gem_header(gem) %> - upgrade to <%= gem.latest_compatible_version.version %>
<% end -%>
<% end -%>
<% if incompatible_gems_by_state[:incompatible] -%>
<%= Rainbow("=> Incompatible with Rails #{rails_version} (with no new compatible versions):").white.bold %>
<%= Rainbow("These gems will need to be removed or replaced before upgrading to Rails #{rails_version}.").italic %>
<% incompatible_gems_by_state[:incompatible].each do |gem| -%>
<%= gem_header(gem) %> - new version, <%= gem.latest_version.version %>, is not compatible with Rails #{rails_version}
<% end -%>
<% end -%>
<% if incompatible_gems_by_state[:no_new_version] -%>
<%= Rainbow("=> Incompatible with Rails #{rails_version} (with no new versions):").white.bold %>
<%= Rainbow("These gems will need to be upgraded by us or removed before upgrading to Rails #{rails_version}.").italic %>
<%= Rainbow("This list is likely to contain internal gems, like Cuddlefish.").italic %>
<% incompatible_gems_by_state[:no_new_version].each do |gem| -%>
<%= gem_header(gem) %> - new version not found
<% end -%>
<% end -%>
<%= Rainbow(incompatible_gems.length.to_s).red %> gems incompatible with Rails <%= rails_version %>
ERB

erb_version = ERB.version
if erb_version =~ /erb.rb \[([\d\.]+) .*\]/
erb_version = $1
end

if Gem::Version.new(erb_version) < Gem::Version.new("2.2")
ERB.new(template, nil, "-").result(binding)
else
ERB.new(template, trim_mode: "-").result(binding)
end
end

def gem_header(_gem)
header = Rainbow("#{_gem.name} #{_gem.version}").bold
header << Rainbow(" (loaded from git)").magenta if _gem.sourced_from_git?
header
end

def incompatible_gems
@incompatible_gems ||= @gems.reject do |gem|
gem.compatible_with_rails?(rails_version: rails_version) || (!include_rails_gems && gem.from_rails?)
end.sort_by { |gem| gem.name }
end

def rails_version
@options[:rails_version]
end

def include_rails_gems
@options[:include_rails_gems]
end
end
40 changes: 40 additions & 0 deletions spec/next_rails/bundle_report/rails_version_compatibility_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe NextRails::BundleReport::RailsVersionCompatibility do
describe "#generate" do
it "returns non incompatible gems" do
output = NextRails::BundleReport::RailsVersionCompatibility.new(options: { rails_version: 7.0 }).generate
expect(output).to match "gems incompatible with Rails 7.0"
end

it "returns incompatible with compatible versions" do
next_rails_version = 7.1
specification = Gem::Specification.new do |s|
s.name = "audited"
s.version = "5.1.0"
s.add_dependency "rails", ">= 5.0", "< 7.1"
end
audited = NextRails::GemInfo.new(specification)
gems = [audited]

allow_any_instance_of(described_class).to receive(:incompatible_gems_by_state)
.and_return({ found_compatible: gems })

allow(audited).to receive(:latest_compatible_version).and_return(Gem::Version.new("5.8.0"))

output =
NextRails::BundleReport::RailsVersionCompatibility.new(
gems: gems,
options: { rails_version: next_rails_version, include_rails_gems: false }
).generate

expect(output).to include("Incompatible with Rails 7.1 (with new versions that are compatible):")
expect(output).to include("These gems will need to be upgraded before upgrading to Rails 7.1.")
expect(output).to include("- upgrade to 5.8.0")
expect(output).to include("gems incompatible with Rails 7.1")
end
end
end

Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

require "spec_helper"
require "next_rails/bundle_report/ruby_version_compatibility"

RSpec.describe NextRails::BundleReport::RubyVersionCompatibility do
let(:ruby_3_0_gem) do
Expand Down
19 changes: 14 additions & 5 deletions spec/next_rails/bundle_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,21 @@
end
end

describe ".compatibility" do
describe "output" do
it "returns ERB generated output" do
output = NextRails::BundleReport.erb_output({}, [], 7.0)
expect(output).to match "gems incompatible with Rails 7.0"
describe ".rails_compatibility" do
it "returns empty output invalid rails version" do
output = with_captured_stdout do
NextRails::BundleReport.rails_compatibility(rails_version: nil)
end
expect(output).to be_empty
end
end

describe ".ruby_compatibility" do
it "returns empty output invalid ruby version" do
output = with_captured_stdout do
NextRails::BundleReport.ruby_compatibility(ruby_version: nil)
end
expect(output).to be_empty
end
end

Expand Down
9 changes: 9 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,12 @@
def with_env(env_hash)
stub_const("ENV", ENV.to_hash.merge!(env_hash))
end

def with_captured_stdout
old_stdout = $stdout
$stdout = StringIO.new
yield
$stdout.string
ensure
$stdout = old_stdout
end

0 comments on commit 13181a7

Please sign in to comment.