Ruby 4.0 removeu gems dos defaults e quebrou dependência transitiva

TLDR: u-case → kind faz require "ostruct" sem declarar a dependência; no Ruby 4.0 ostruct deixou de ser default gem e a suíte parou de bootar. Correção: declarar ostruct no Gemfile do app.

O que aconteceu

A suíte do modules/backend deixou de bootar com cannot load such file. O erro não vinha de código nosso nem de uma gem que declaramos — vinha de uma dependência transitiva.

Causa raiz

No Ruby 4.0, várias gems de stdlib deixaram de ser default gems (ostruct, entre outras). Gems que fazem require delas sem declará-las no gemspec quebram no boot.

O caso concreto: u-case depende de kind, e kind 6.0.1 faz require "ostruct" sem declarar a dependência — o gemspec dela não tem nenhuma dependência de runtime. Até o Ruby 3.4 isso funcionava por acidente, porque ostruct estava sempre disponível como default gem.

mermaid graph LR A["app (Gemfile)"] --> B["u-case"] B --> C["kind 6.0.1"] C -.->|"require 'ostruct'<br/>não declarado"| D["ostruct"] D -->|"Ruby ≤ 3.4"| E["default gem ✓"] D -->|"Ruby 4.0"| F["ausente ✗"] style F fill:#7f1d1d,color:#fff

Correção

Declarar a gem no Gemfile do app até a dependência corrigir o próprio gemspec:

ruby # stdlib gem removed from Ruby 4.0 defaults, still required by u-case's kind dependency gem "ostruct"

Como evitar

  • O próprio Ruby avisa antes de quebrar: X used to be loaded from the standard library, but is not part of the default gems since Ruby 4.0.0. Tratar esse warning como erro em CI antecipa o problema para antes do upgrade.
  • Ao subir a versão do Ruby, o erro de boot indica qual gem tentou o require — a correção é sempre no Gemfile do app, com comentário explicando por que a gem está lá (senão ela parece órfã e alguém a remove).
  • O mesmo padrão vale para as outras gems que saíram dos defaults, não só ostruct.