Replace rainbow gem with native NextRails::Tint wrapper#183
Replace rainbow gem with native NextRails::Tint wrapper#183
Conversation
Drop the rainbow runtime dependency in favor of a small (~30 LOC) ANSI wrapper class, NextRails::Tint, with a chainable styling API. - Add lib/next_rails/tint.rb (chainable, pure Ruby, 2.3+ compatible) - Replace all Rainbow(x) call sites with NextRails::Tint[x] - Remove rainbow from next_rails.gemspec - Refactor RubyVersionCompatibility#message to a heredoc - Refactor RailsVersionCompatibility#gem_header to Array#join - Add spec/next_rails/tint_spec.rb - Update CHANGELOG
Ensures #generate always returns a String, matching the heredoc-based #message branch and existing spec expectations using String#include?. Previously, invalid_message returned a NextRails::Tint instance, which worked with the old Rainbow gem because its Presenter inherited from String. NextRails::Tint does not, so call .to_s at the boundary.
Keep internal helpers Tint-native and stringify only at the public return point. One conversion site, contract lives where the public API is defined.
etagwerker
left a comment
There was a problem hiding this comment.
Nice cleanup. The rationale for dropping the runtime dep is solid, and Tint is small enough to maintain. A few questions and suggestions inline.
One thing I couldn't comment inline on (since the file isn't in the diff): Gemfile.lock still shows rainbow (>= 3) and rainbow (3.1.1). Could you regenerate it with bundle install so the lockfile matches the gemspec?
Also, the test plan still has two unchecked manual smoke checks. Could you confirm bundle_report compatible / bundle_report outdated / the deprecation_tracker compare-mode output all render with the expected colors before we merge?
| white: 37 | ||
| }.freeze | ||
|
|
||
| def self.[](string) |
There was a problem hiding this comment.
Why do we add this "constructor" method? Is it a convention taken from somewhere else, I have not come across this commonly in other projects. The original rainbow gem (it seems) made use of the standard initialize constructor, so I don't currently see the value of NextRails::Tint["hello"] vs NextRails::Tint("hello")
There was a problem hiding this comment.
I know it looks weird but it is used I think TTY::Color gem or even ruby core Array[1,2,3], Hash[:a,1], Set[1,2,3]
I updated to use() anyways.
The lockfile is gitignored, that was in your local env, just run bundle install
Confirmed. Smokes pass with expected colors:
|
- Switch Tint construction from `Tint[str]` to `NextRails::Tint(str)` factory on the module, mirroring Kernel-style wrappers like `Rainbow(...)`, `Pathname(...)`, `BigDecimal(...)` without polluting Kernel. - Make Tint effectively immutable: style methods return a new instance carrying `@codes + [code]` instead of mutating `@codes` on self, so a shared reference can't accumulate styles across chains. - Add `require "next_rails/tint"` to cli.rb and rails_version_compatibility.rb so every call-site file is loadable standalone. - Rewrite RubyVersionCompatibility#message with an array + join instead of a heredoc so the empty-incompatible case keeps the original single-blank-line spacing between header and footer. - Lock the deprecation_tracker compare-mode raise format with a spec asserting the message is wrapped in red ANSI escape codes (the raise relies on Tint#to_str coercion, so this contract is load-bearing). - Add a Tint spec asserting style chains do not mutate a shared base.
a0ad793 to
73705fb
Compare
Summary
Replaces the
rainbowruntime dependency with a small native ANSI wrapper,NextRails::Tint, dropping an external gem in favor of ~30 lines of pure Ruby.Why drop the dependency
next_railsis an upgrade toolkit that has to run inside other people's Gemfiles across Ruby 2.3 → 4.0. Every runtime dependency we carry is one more constraint on the host application's bundle, one more compatibility surface to maintain across that long Ruby range, and one more thing that can conflict during the very upgrade we're trying to help with. The less we ask of the host environment, the less intrusive we are. Color formatting is trivial enough (a handful of ANSI escape codes) that pulling in an external gem isn't worth the cost.NextRails::Tintclass (lib/next_rails/tint.rb) with a chainable styling API:NextRails::Tint("text").red.bold. Style methods return new instances, soTintis effectively immutable and chains never accumulate codes on a shared reference.NextRails.Tint(string)so call sites readNextRails::Tint("hello").red.bold(no Kernel pollution, mirrors howRainbow(...)was invoked).Rainbow(...)call sites updated toNextRails::Tint(...)rainbowremoved fromnext_rails.gemspecRubyVersionCompatibility#messageandRailsVersionCompatibility#gem_headertoArray#join, removing the need forTint#+/Tint#<<and keeping the wrapper's surface minimal (constructor + style chain +to_s/to_str)spec/next_rails/tint_spec.rb(covers immutability) andspec/deprecation_tracker_spec.rb(locks ANSI escape codes in the raisedUnexpectedDeprecationsmessage via Ruby'sto_strcoercion onraise)require "next_rails/tint"(no global require) so files stay loadable standalonefrozen_string_literal, squiggly heredoc,Hash#freeze,define_method)Test plan
bundle exec rakepasses locally on a supported Rubybundle_report outdatedrenders gem names wrapped in\e[1;37m...\e[0m;bundle_report compatibilityrenders the incompatible count wrapped in\e[31m...\e[0mdeprecation_trackercompare-mode error still rendered red — locked by new spec asserting the raised message starts with\e[31mand ends with\e[0m