From 9ade112dbebe3fcf60a2df546987977f90e22104 Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Tue, 11 Feb 2025 15:03:03 -0600 Subject: [PATCH 1/4] 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 --- CHANGELOG.md | 1 + exe/bundle_report | 4 +- lib/next_rails.rb | 1 + lib/next_rails/bundle_report.rb | 67 ++------------- .../rails_version_compatibility.rb | 84 +++++++++++++++++++ .../rails_version_compatibility_spec.rb | 13 +++ .../ruby_version_compatibility_spec.rb | 1 - spec/next_rails/bundle_report_spec.rb | 17 ++-- 8 files changed, 120 insertions(+), 68 deletions(-) create mode 100644 lib/next_rails/bundle_report/rails_version_compatibility.rb create mode 100644 spec/next_rails/bundle_report/rails_version_compatibility_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index caffef8..2942347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ # v1.4.3 / 2025-02-20 [(commits)](https://github.com/fastruby/next_rails/compare/v1.4.2...v1.4.3) +- [Move rails_version compatibility to its own class](https://github.com/fastruby/next_rails/pull/137) - [Add next_rails --init](https://github.com/fastruby/next_rails/pull/139) - [Add Ruby 3.4 support](https://github.com/fastruby/next_rails/pull/133) diff --git a/exe/bundle_report b/exe/bundle_report index 0708464..25bbc8a 100755 --- a/exe/bundle_report +++ b/exe/bundle_report @@ -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 diff --git a/lib/next_rails.rb b/lib/next_rails.rb index 27412cf..ec8481e 100644 --- a/lib/next_rails.rb +++ b/lib/next_rails.rb @@ -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 diff --git a/lib/next_rails/bundle_report.rb b/lib/next_rails/bundle_report.rb index 771035c..76fec87 100644 --- a/lib/next_rails/bundle_report.rb +++ b/lib/next_rails/bundle_report.rb @@ -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) diff --git a/lib/next_rails/bundle_report/rails_version_compatibility.rb b/lib/next_rails/bundle_report/rails_version_compatibility.rb new file mode 100644 index 0000000..8d661ef --- /dev/null +++ b/lib/next_rails/bundle_report/rails_version_compatibility.rb @@ -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 diff --git a/spec/next_rails/bundle_report/rails_version_compatibility_spec.rb b/spec/next_rails/bundle_report/rails_version_compatibility_spec.rb new file mode 100644 index 0000000..cf522c3 --- /dev/null +++ b/spec/next_rails/bundle_report/rails_version_compatibility_spec.rb @@ -0,0 +1,13 @@ +# 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 + end +end + diff --git a/spec/next_rails/bundle_report/ruby_version_compatibility_spec.rb b/spec/next_rails/bundle_report/ruby_version_compatibility_spec.rb index ff416f3..be617c9 100644 --- a/spec/next_rails/bundle_report/ruby_version_compatibility_spec.rb +++ b/spec/next_rails/bundle_report/ruby_version_compatibility_spec.rb @@ -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 diff --git a/spec/next_rails/bundle_report_spec.rb b/spec/next_rails/bundle_report_spec.rb index e159a7a..606168e 100644 --- a/spec/next_rails/bundle_report_spec.rb +++ b/spec/next_rails/bundle_report_spec.rb @@ -64,12 +64,17 @@ 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" - end + describe ".rails_compatibility" do + it "returns nil for invalid rails version" do + output = NextRails::BundleReport.rails_compatibility(rails_version: nil) + expect(output).to eq(nil) + end + end + + describe ".ruby_compatibility" do + it "returns nil for invalid ruby version" do + output = NextRails::BundleReport.ruby_compatibility(ruby_version: nil) + expect(output).to eq(nil) end end From 6a650162aafb11f25442169cd6226afc454704d2 Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Mon, 3 Mar 2025 15:28:06 -0600 Subject: [PATCH 2/4] Add incompatible with new compatible versions --- .../rails_version_compatibility_spec.rb | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/spec/next_rails/bundle_report/rails_version_compatibility_spec.rb b/spec/next_rails/bundle_report/rails_version_compatibility_spec.rb index cf522c3..eda4ca4 100644 --- a/spec/next_rails/bundle_report/rails_version_compatibility_spec.rb +++ b/spec/next_rails/bundle_report/rails_version_compatibility_spec.rb @@ -3,11 +3,38 @@ require "spec_helper" RSpec.describe NextRails::BundleReport::RailsVersionCompatibility do - describe "generate" 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 From ec3b988bf55294db3d7c5c6bc1413e36aba330a2 Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Mon, 3 Mar 2025 15:37:52 -0600 Subject: [PATCH 3/4] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2942347..667bfcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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/) +- [Move rails_version compatibility to its own class](https://github.com/fastruby/next_rails/pull/137) * Your changes/patches go here. @@ -10,7 +11,6 @@ # v1.4.3 / 2025-02-20 [(commits)](https://github.com/fastruby/next_rails/compare/v1.4.2...v1.4.3) -- [Move rails_version compatibility to its own class](https://github.com/fastruby/next_rails/pull/137) - [Add next_rails --init](https://github.com/fastruby/next_rails/pull/139) - [Add Ruby 3.4 support](https://github.com/fastruby/next_rails/pull/133) From 550793d549640cb7907bd000ec965f6931b361ee Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Wed, 5 Mar 2025 07:56:01 -0600 Subject: [PATCH 4/4] Capture stdout to assert Co-authored-by: Ariel --- spec/next_rails/bundle_report_spec.rb | 16 ++++++++++------ spec/spec_helper.rb | 9 +++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/spec/next_rails/bundle_report_spec.rb b/spec/next_rails/bundle_report_spec.rb index 606168e..6eeb17a 100644 --- a/spec/next_rails/bundle_report_spec.rb +++ b/spec/next_rails/bundle_report_spec.rb @@ -65,16 +65,20 @@ end describe ".rails_compatibility" do - it "returns nil for invalid rails version" do - output = NextRails::BundleReport.rails_compatibility(rails_version: nil) - expect(output).to eq(nil) + 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 nil for invalid ruby version" do - output = NextRails::BundleReport.ruby_compatibility(ruby_version: nil) - expect(output).to eq(nil) + 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 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 819479b..1c55882 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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